diff --git a/README.md b/README.md
index 53e82356..24644c94 100644
--- a/README.md
+++ b/README.md
@@ -250,6 +250,37 @@ A **bext Cloud** view in Dev mode integrates the hosted control plane at [cloud.
Each SSH connection has a **bext** hover action that opens the Instance tab for that box. (v1 targets the local loopback `http://127.0.0.1` — managing a remote box over an SSH tunnel is the next step.) Palette: **bext Cloud : se connecter / ouvrir**.
+## Use Codex or Claude Code with a subscription
+
+In **Settings → AI**, choose **Codex CLI (ChatGPT subscription)** or
+**Claude Code CLI (subscription)**. These backends reuse the provider CLI's
+local login and do not require a ShellDeck API key.
+
+Install the CLI and sign in on the computer where it will run:
+
+```sh
+# Codex: sign in with your eligible ChatGPT account
+codex login
+codex login status
+
+# Claude Code: sign in with your eligible Claude subscription
+claude auth login --claudeai
+claude auth status
+```
+
+Settings shows the matching setup command, a copy button, the official sign-in
+guide, and an explicit connection test. The **Agents** empty state shows the same
+setup for its selected provider. For an SSH target, install and sign in on that
+host; ShellDeck does not forward this computer's provider credentials.
+
+The CLI determines authentication and billing. Existing API credentials in its
+configuration or environment can select API usage instead of the subscription;
+check the active login and remove that configuration when using your plan.
+Subscription limits still apply. The separate **OpenAI API** and **Anthropic
+API** backends continue to use OS-keychain API keys.
+See [Codex authentication](https://developers.openai.com/codex/auth) and
+[Claude Code authentication](https://code.claude.com/docs/en/authentication).
+
## Keyboard Shortcuts
Use `Ctrl` on Linux/Windows and `Cmd` on macOS unless noted otherwise.
diff --git a/crates/shelldeck-core/locales/en.toml b/crates/shelldeck-core/locales/en.toml
index 28928ea5..3e120345 100644
--- a/crates/shelldeck-core/locales/en.toml
+++ b/crates/shelldeck-core/locales/en.toml
@@ -2257,3 +2257,15 @@ bext.error.instance_not_found = "Resource or SDK route not found on the Bext ins
bext.error.instance_server = "The Bext instance hit an error. Check its logs, then try again."
bext.error.instance_bad_response = "Unexpected response from the Bext instance. Details are in the logs."
bext.error.instance_other = "The request to the Bext instance failed. Details are in the logs."
+
+# Subscription-backed local CLI setup
+settings.ai.backend.claude_cli = "Claude Code CLI (subscription)"
+settings.ai.backend.codex_cli = "Codex CLI (ChatGPT subscription)"
+ai.subscription.claude = "Use your eligible Claude subscription through Claude Code. No API key is required in ShellDeck. Your subscription limits apply."
+ai.subscription.codex = "Use your eligible ChatGPT subscription through Codex. No API key is required in ShellDeck. Your subscription limits apply."
+ai.subscription.local = "Install the CLI, then run this command in a terminal on this computer and complete sign-in in your browser."
+ai.subscription.remote = "Install the CLI and sign in on the selected SSH host. This computer’s login is not forwarded to the remote host."
+ai.subscription.copy = "Copy command"
+ai.subscription.docs = "Sign-in guide"
+ai.subscription.status = "Check the active account: %{command}"
+ai.subscription.billing = "ShellDeck reuses the CLI login. An API key configured in the CLI or its environment can select API billing: check the active account and remove that configuration to use your subscription."
diff --git a/crates/shelldeck-core/locales/fr.toml b/crates/shelldeck-core/locales/fr.toml
index 914a9a9d..f32d4de6 100644
--- a/crates/shelldeck-core/locales/fr.toml
+++ b/crates/shelldeck-core/locales/fr.toml
@@ -2257,3 +2257,15 @@ bext.error.instance_not_found = "Ressource ou route SDK introuvable sur l’Inst
bext.error.instance_server = "L’Instance Bext a rencontré une erreur. Consultez ses journaux puis réessayez."
bext.error.instance_bad_response = "Réponse inattendue de l’Instance Bext. Le détail est dans les journaux."
bext.error.instance_other = "La requête vers l’Instance Bext a échoué. Le détail est dans les journaux."
+
+# Subscription-backed local CLI setup
+settings.ai.backend.claude_cli = "Claude Code CLI (abonnement)"
+settings.ai.backend.codex_cli = "Codex CLI (abonnement ChatGPT)"
+ai.subscription.claude = "Utilisez votre abonnement Claude éligible via Claude Code. Aucune clé API à saisir dans ShellDeck. Les limites de votre abonnement s’appliquent."
+ai.subscription.codex = "Utilisez votre abonnement ChatGPT éligible via Codex. Aucune clé API à saisir dans ShellDeck. Les limites de votre abonnement s’appliquent."
+ai.subscription.local = "Installez la CLI, puis exécutez cette commande dans un terminal sur cet ordinateur et terminez la connexion dans le navigateur."
+ai.subscription.remote = "Installez la CLI et connectez-vous sur l’hôte SSH sélectionné. La connexion de cet ordinateur n’est pas transmise à l’hôte distant."
+ai.subscription.copy = "Copier la commande"
+ai.subscription.docs = "Guide de connexion"
+ai.subscription.status = "Vérifier le compte utilisé : %{command}"
+ai.subscription.billing = "ShellDeck réutilise la connexion de la CLI. Une clé API configurée dans la CLI ou son environnement peut choisir la facturation API : vérifiez le compte utilisé et retirez cette configuration pour utiliser votre abonnement."
diff --git a/crates/shelldeck-core/src/ai.rs b/crates/shelldeck-core/src/ai.rs
index c1d0a2bf..b5edf3f2 100644
--- a/crates/shelldeck-core/src/ai.rs
+++ b/crates/shelldeck-core/src/ai.rs
@@ -2339,6 +2339,79 @@ mod tests {
std::fs::remove_file(path).unwrap();
}
+ // SDTEST-1935 — real provider-owned subscription credentials, no API keys.
+ #[cfg(unix)]
+ #[test]
+ #[ignore = "requires locally signed-in Codex/Claude subscriptions and sends two minimal completions"]
+ fn live_subscription_clis_complete_without_api_keys() {
+ const CHILD: &str = "SHELLDECK_SUBSCRIPTION_SMOKE_CHILD";
+ assert_eq!(
+ std::env::var("SHELLDECK_LIVE_SUBSCRIPTION").as_deref(),
+ Ok("1")
+ );
+ if std::env::var(CHILD).as_deref() != Ok("1") {
+ // Never mutate process-wide environment alongside other Rust tests.
+ // Retain HOME/CODEX_HOME/CLAUDE_CONFIG_DIR: the CLIs own OAuth storage.
+ let mut child = Command::new(std::env::current_exe().unwrap());
+ child
+ .args([
+ "--exact",
+ "ai::tests::live_subscription_clis_complete_without_api_keys",
+ "--ignored",
+ "--nocapture",
+ ])
+ .env(CHILD, "1");
+ for key in [
+ "OPENAI_API_KEY",
+ "CODEX_API_KEY",
+ "ANTHROPIC_API_KEY",
+ "ANTHROPIC_AUTH_TOKEN",
+ ] {
+ child.env_remove(key);
+ }
+ let result = child.output().unwrap();
+ assert!(String::from_utf8_lossy(&result.stdout).contains("running 1 test"));
+ assert!(
+ result.status.success(),
+ "subscription smoke failed: {}\n{}",
+ String::from_utf8_lossy(&result.stdout),
+ String::from_utf8_lossy(&result.stderr)
+ );
+ return;
+ }
+ let codex = Command::new("codex")
+ .args(["login", "status"])
+ .output()
+ .unwrap();
+ assert!(codex.status.success());
+ assert!(
+ format!(
+ "{}{}",
+ String::from_utf8_lossy(&codex.stdout),
+ String::from_utf8_lossy(&codex.stderr)
+ )
+ .contains("ChatGPT"),
+ "Codex must be signed in with ChatGPT before this smoke"
+ );
+ let claude = Command::new("claude")
+ .args(["auth", "status"])
+ .output()
+ .unwrap();
+ assert!(claude.status.success());
+ let status: Value = serde_json::from_slice(&claude.stdout).unwrap();
+ assert_eq!(status["loggedIn"], true);
+ assert_eq!(status["authMethod"], "claude.ai");
+ for backend in [AiBackend::CodexCli, AiBackend::ClaudeCli] {
+ let config = AiConfig {
+ enabled: true,
+ backend,
+ ..AiConfig::default()
+ };
+ // create_client must not require ShellDeck's OS-keychain API key.
+ assert_eq!(test_connection(&config).unwrap().text, "SHELLDECK_AI_OK");
+ }
+ }
+
// SDTEST-1338
#[cfg(unix)]
#[test]
diff --git a/crates/shelldeck-ui/src/agent_console_view.rs b/crates/shelldeck-ui/src/agent_console_view.rs
index 0e3f116f..92c4cae6 100644
--- a/crates/shelldeck-ui/src/agent_console_view.rs
+++ b/crates/shelldeck-ui/src/agent_console_view.rs
@@ -2327,31 +2327,41 @@ impl Render for AgentConsoleView {
.px(if wide { px(24.0) } else { px(12.0) })
.py(px(16.0));
if !has_content {
- output = output.child(
- div()
- .flex()
- .flex_col()
- .items_center()
- .justify_center()
- .flex_1()
- .gap(px(8.0))
- .text_color(ShellDeckColors::text_muted())
- .child(lucide_icon("bot", 27.0, ShellDeckColors::primary()))
- .child(
- div()
- .text_size(px(14.0))
- .font_weight(FontWeight::SEMIBOLD)
- .text_color(ShellDeckColors::text_primary())
- .child(t!("agents.empty.title").to_string()),
- )
- .child(
- div()
- .max_w(px(520.0))
- .text_size(px(11.0))
- .text_align(TextAlign::Center)
- .child(t!("agents.empty.description").to_string()),
- ),
+ let subscription_setup = crate::cli_subscription_setup::render_subscription_setup(
+ context.provider.binary(),
+ matches!(context.target, AgentTarget::Ssh { .. }),
);
+ let mut empty_state = div()
+ .flex()
+ .flex_col()
+ .items_center()
+ .justify_center()
+ .gap(px(8.0))
+ .text_color(ShellDeckColors::text_muted())
+ .child(lucide_icon("bot", 27.0, ShellDeckColors::primary()))
+ .child(
+ div()
+ .text_size(px(14.0))
+ .font_weight(FontWeight::SEMIBOLD)
+ .text_color(ShellDeckColors::text_primary())
+ .child(t!("agents.empty.title").to_string()),
+ )
+ .child(
+ div()
+ .max_w(px(520.0))
+ .text_size(px(11.0))
+ .text_align(TextAlign::Center)
+ .child(t!("agents.empty.description").to_string()),
+ );
+ if subscription_setup.is_some() {
+ empty_state = empty_state.flex_shrink_0().py(px(16.0));
+ } else {
+ empty_state = empty_state.flex_1();
+ }
+ output = output.child(empty_state);
+ if let Some(setup) = subscription_setup {
+ output = output.child(div().w_full().max_w(px(520.0)).mx_auto().child(setup));
+ }
} else {
let mut timeline_view = div()
.w_full()
diff --git a/crates/shelldeck-ui/src/cli_subscription_setup.rs b/crates/shelldeck-ui/src/cli_subscription_setup.rs
new file mode 100644
index 00000000..07af242a
--- /dev/null
+++ b/crates/shelldeck-ui/src/cli_subscription_setup.rs
@@ -0,0 +1,83 @@
+use crate::{scale::px, t, theme::ShellDeckColors};
+use adabraka_ui::prelude::{Button, ButtonSize, ButtonVariant};
+use gpui::{div, prelude::*, ClipboardItem, Div};
+
+/// The provider CLI owns login and credentials, on the host where it runs.
+/// Showing setup never starts a login or a model request.
+pub(crate) fn render_subscription_setup(command: &str, remote: bool) -> Option
{
+ let (description, login, status, docs) = match command {
+ "claude" => (
+ t!("ai.subscription.claude").to_string(),
+ "claude auth login --claudeai",
+ "claude auth status",
+ "https://code.claude.com/docs/en/authentication",
+ ),
+ "codex" => (
+ t!("ai.subscription.codex").to_string(),
+ "codex login",
+ "codex login status",
+ "https://developers.openai.com/codex/auth",
+ ),
+ _ => return None,
+ };
+ let location = if remote {
+ t!("ai.subscription.remote")
+ } else {
+ t!("ai.subscription.local")
+ };
+ Some(
+ div()
+ .flex()
+ .flex_col()
+ .flex_shrink_0()
+ .w_full()
+ .min_w(px(0.0))
+ .gap(px(8.0))
+ .p(px(12.0))
+ .rounded_md()
+ .border_1()
+ .border_color(ShellDeckColors::border())
+ .text_size(px(12.0))
+ .text_color(ShellDeckColors::text_primary())
+ .child(description)
+ .child(location.to_string())
+ .child(
+ div()
+ .flex()
+ .flex_wrap()
+ .items_center()
+ .gap(px(8.0))
+ .child(div().font_family("JetBrains Mono").child(login))
+ .child(
+ Button::new(
+ "subscription-copy-login",
+ t!("ai.subscription.copy").to_string(),
+ )
+ .size(ButtonSize::Sm)
+ .variant(ButtonVariant::Outline)
+ .on_click(move |_, _, cx| {
+ cx.write_to_clipboard(ClipboardItem::new_string(login.to_string()));
+ }),
+ )
+ .child(
+ Button::new(
+ "subscription-login-docs",
+ t!("ai.subscription.docs").to_string(),
+ )
+ .size(ButtonSize::Sm)
+ .variant(ButtonVariant::Ghost)
+ .on_click(move |_, _, cx| cx.open_url(docs)),
+ ),
+ )
+ .child(
+ div()
+ .text_color(ShellDeckColors::text_muted())
+ .child(t!("ai.subscription.status", command = status).to_string()),
+ )
+ .child(
+ div()
+ .text_color(ShellDeckColors::text_muted())
+ .child(t!("ai.subscription.billing").to_string()),
+ ),
+ )
+}
diff --git a/crates/shelldeck-ui/src/lib.rs b/crates/shelldeck-ui/src/lib.rs
index 59937dbc..47ada8ec 100644
--- a/crates/shelldeck-ui/src/lib.rs
+++ b/crates/shelldeck-ui/src/lib.rs
@@ -7,6 +7,7 @@ pub mod ai_workflow;
pub mod attachment_annotator;
pub mod bext_cloud_view;
pub mod brand;
+mod cli_subscription_setup;
pub mod command_palette;
pub mod command_palette_window;
pub mod connection_combobox;
diff --git a/crates/shelldeck-ui/src/settings.rs b/crates/shelldeck-ui/src/settings.rs
index 5fcba7ac..9dfcc8cb 100644
--- a/crates/shelldeck-ui/src/settings.rs
+++ b/crates/shelldeck-ui/src/settings.rs
@@ -1455,7 +1455,7 @@ impl SettingsView {
.child(Self::render_setting_row(
t!("settings.ai.backend.label").as_ref(),
t!("settings.ai.backend.description").as_ref(),
- div().w(px(220.0)).child(self.ai_backend_select.clone()),
+ div().w(px(300.0)).child(self.ai_backend_select.clone()),
))
.child(Self::render_setting_row(
t!("settings.ai.status.label").as_ref(),
@@ -1481,6 +1481,12 @@ impl SettingsView {
div().w(px(220.0)).child(model_input),
));
+ if let Some(setup) = backend.cli_command().and_then(|command| {
+ crate::cli_subscription_setup::render_subscription_setup(command, false)
+ }) {
+ root = root.child(setup);
+ }
+
if let Some(provider) = backend.provider_key() {
let key_state = self.ai_api_key_state.clone();
let provider = provider.to_string();
@@ -3098,8 +3104,14 @@ fn build_ai_backend_select(
AiBackend::Disabled,
t!("settings.ai.backend.disabled").to_string(),
),
- (AiBackend::ClaudeCli, "Claude Code CLI".to_string()),
- (AiBackend::CodexCli, "Codex CLI".to_string()),
+ (
+ AiBackend::ClaudeCli,
+ t!("settings.ai.backend.claude_cli").to_string(),
+ ),
+ (
+ AiBackend::CodexCli,
+ t!("settings.ai.backend.codex_cli").to_string(),
+ ),
(AiBackend::AiderCli, "Aider CLI".to_string()),
(AiBackend::AutomoniqueAcp, "Automonique ACP".to_string()),
(AiBackend::OpenAi, "OpenAI API".to_string()),
diff --git a/docs/design/reliability-test-tracker.md b/docs/design/reliability-test-tracker.md
index 52c151d5..8d33a0de 100644
--- a/docs/design/reliability-test-tracker.md
+++ b/docs/design/reliability-test-tracker.md
@@ -30,6 +30,14 @@ risques par frontière observable et impose une vérification avant correction.
## Journal
+- **2026-09-30 — Abonnements Codex et Claude Code vérifiés.** Les clients CLI
+ réutilisaient déjà l’authentification du fournisseur, sans clé ShellDeck,
+ mais l’interface n’expliquait pas ce parcours. Réglages et Agents partagent
+ maintenant le guide connexion/copier/statut et la frontière hôte SSH.
+ SDTEST-1935 a effectué deux complétions réelles avec login ChatGPT et
+ Claude.ai/Max après retrait des variables de clés API. Ce test reste opt-in
+ hors CI ; SDTEST-1936 reste Rouge pour le harnais visuel automatisé.
+
- **2026-09-30 — Dernier passage, champs secrets.** Le formulaire de connexion
et la clé API démarraient sans masquage : le bouton œil ne configurait pas
le type de l’état. Les deux états sont maintenant initialisés en Password.
diff --git a/docs/testing/USE_CASES.md b/docs/testing/USE_CASES.md
index 1fc52b39..1c1b2554 100644
--- a/docs/testing/USE_CASES.md
+++ b/docs/testing/USE_CASES.md
@@ -880,6 +880,10 @@ The session search has a usable text viewport, and inherited navigator/provider
labels follow the active theme in both light and dark appearances
(SDTEST-1931/1932, manual native validation).
+The empty Claude Code/Codex session shows the shared subscription sign-in
+guide. SSH targets require their own installed CLI and provider login; local
+provider credentials are never forwarded to them.
+
Dev mode exposes one provider-neutral agent console for Claude Code, Codex,
Automonique ACP, DeepSeek through Jcode, and Jcode's configured/default provider.
Every run names a target-valid working directory (a native absolute path locally,
@@ -1702,7 +1706,13 @@ the sidebar connection action. Unknown and malformed menu ids are ignored.
Settings → IA selects one local CLI (Claude Code, Codex, Aider) or API
provider (OpenAI, Anthropic), an optional model, and per-surface opt-outs. API
-keys live only in the OS keychain. The assistant affordance remains hidden
+keys live only in the OS keychain. Codex and Claude Code CLI backends also
+reuse their provider-owned subscription login, without requiring a ShellDeck
+API key. Settings labels those options and shows sign-in/copy/status guidance;
+API billing remains a separate choice and CLI-configured credentials can
+override subscription usage. A gated live smoke checks both subscription
+methods before completing with API-key environment variables removed.
+The assistant affordance remains hidden
when AI or the current surface is disabled, or when a selected local CLI is
not executable. The explicit connection test sends a real minimal completion
and reports the provider/model result, but does not become a per-launch lock.
diff --git a/docs/testing/screenshots/2026-09-30/subscriptions/agents-setup.png b/docs/testing/screenshots/2026-09-30/subscriptions/agents-setup.png
new file mode 100644
index 00000000..1d91bccd
Binary files /dev/null and b/docs/testing/screenshots/2026-09-30/subscriptions/agents-setup.png differ
diff --git a/docs/testing/screenshots/2026-09-30/subscriptions/backend-options.png b/docs/testing/screenshots/2026-09-30/subscriptions/backend-options.png
new file mode 100644
index 00000000..cdc3a4c1
Binary files /dev/null and b/docs/testing/screenshots/2026-09-30/subscriptions/backend-options.png differ
diff --git a/docs/testing/screenshots/2026-09-30/subscriptions/claude-settings.png b/docs/testing/screenshots/2026-09-30/subscriptions/claude-settings.png
new file mode 100644
index 00000000..8c5fdb67
Binary files /dev/null and b/docs/testing/screenshots/2026-09-30/subscriptions/claude-settings.png differ
diff --git a/docs/testing/screenshots/2026-09-30/subscriptions/codex-compact.png b/docs/testing/screenshots/2026-09-30/subscriptions/codex-compact.png
new file mode 100644
index 00000000..546000f2
Binary files /dev/null and b/docs/testing/screenshots/2026-09-30/subscriptions/codex-compact.png differ
diff --git a/docs/testing/screenshots/2026-09-30/subscriptions/codex-settings.png b/docs/testing/screenshots/2026-09-30/subscriptions/codex-settings.png
new file mode 100644
index 00000000..5b8ea452
Binary files /dev/null and b/docs/testing/screenshots/2026-09-30/subscriptions/codex-settings.png differ
diff --git a/docs/testing/subscription-qa-2026-09-30.md b/docs/testing/subscription-qa-2026-09-30.md
new file mode 100644
index 00000000..3cd78fec
--- /dev/null
+++ b/docs/testing/subscription-qa-2026-09-30.md
@@ -0,0 +1,70 @@
+# Subscription CLI verification — 2026-09-30
+
+ShellDeck already delegates Claude Code and Codex authentication to their
+installed CLIs. Neither CLI branch calls the OS-keychain API-key path. This
+pass proves that the contextual assistant works through subscription logins and
+makes that setup discoverable in Settings and the Agents empty state.
+
+## Live subscription check
+
+SDTEST-1935 ran the real ShellDeck `create_client` → `complete` →
+`test_connection` path against Codex CLI 0.159.2 and Claude Code 2.1.285.
+A child test process removed `OPENAI_API_KEY`, `CODEX_API_KEY`,
+`ANTHROPIC_API_KEY`, and `ANTHROPIC_AUTH_TOKEN`, preserving the providers’
+normal OAuth storage. CLI status first confirmed ChatGPT authentication for
+Codex and Claude.ai/Max for Claude Code. Both returned exactly
+`SHELLDECK_AI_OK`. No API key was entered or stored in ShellDeck.
+
+Repeat only on a computer already signed in with both eligible subscriptions:
+
+```sh
+PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig \
+SHELLDECK_LIVE_SUBSCRIPTION=1 \
+cargo test -p shelldeck-core --lib \
+ ai::tests::live_subscription_clis_complete_without_api_keys \
+ -- --ignored --exact --nocapture
+```
+
+This test is ignored by default: it needs the user's subscriptions and sends
+two real, minimal completions. It never starts a login, replaces credentials,
+or runs a write-capable coding agent. Provider CLIs may refresh their own tokens.
+
+## Native interface check
+
+The actual app ran on a private X11 display with a temporary XDG app profile,
+fictional Manage account and API-key variables removed. At 1200 × 850 and
+580 × 600:
+
+- [Both subscription choices](screenshots/2026-09-30/subscriptions/backend-options.png)
+ fit without clipping; the API providers remain separate options.
+- [Claude Code](screenshots/2026-09-30/subscriptions/claude-settings.png) and
+ [Codex](screenshots/2026-09-30/subscriptions/codex-settings.png) show their
+ sign-in command, account-status command, official guide, quota reminder,
+ and API-credential billing warning. Neither shows an API-key input.
+- Both copy buttons produced the exact command in the private display's
+ clipboard: `claude auth login --claudeai` and `codex login`.
+- [Compact Settings](screenshots/2026-09-30/subscriptions/codex-compact.png)
+ retains the setup controls through scrolling.
+- [Agents](screenshots/2026-09-30/subscriptions/agents-setup.png) uses the same
+ component. Its guide is fully visible at desktop size; for an SSH target,
+ setup belongs on the selected host rather than forwarding local credentials.
+
+The normal workspace suite still passes 853 tests; seven live/infrastructure
+cases and two doc examples are ignored. Linux build/check, Clippy with
+`--no-deps -- -D warnings`, formatting and whitespace checks pass.
+SDTEST-1936 remains Red for automated native render/click coverage.
+
+## Boundaries
+
+Authentication and billing remain owned by the CLI. Configured API credentials
+can select API billing; the interface tells users to check their active account.
+This pass does not force a different login, promise access outside a plan's
+entitlements, or remove the independent API backends. Native browser sign-in,
+remote subscription login and full writable coding-agent journeys were not
+live-tested. No production ticket, request, source file or remote service was
+modified by the two completion checks.
+
+Official behavior was checked against [Codex authentication](https://developers.openai.com/codex/auth),
+[Codex CLI commands](https://developers.openai.com/codex/cli/reference),
+[Claude Code authentication](https://code.claude.com/docs/en/authentication),
+and [Claude Code credential precedence](https://code.claude.com/docs/en/env-vars).
diff --git a/docs/testing/tests-core.md b/docs/testing/tests-core.md
index 0793ce1e..9fcd281f 100644
--- a/docs/testing/tests-core.md
+++ b/docs/testing/tests-core.md
@@ -370,6 +370,7 @@ Existing: **0 tests**.
| ID | Location | SDUC | Status | Notes |
|---|---|---|---|---|
| SDTEST-1338 | `ai.rs::fake_local_clis_complete_the_real_connection_test_path` | SDUC-413, SDUC-416 | Green | Fake executable Claude/Codex clients traverse config → subprocess → provider parsing → exact connection-test response without contacting a real provider. |
+| SDTEST-1935 | `ai.rs::live_subscription_clis_complete_without_api_keys` (`#[ignore]`, `SHELLDECK_LIVE_SUBSCRIPTION=1`) | SDUC-413, SDUC-416 | Green (live, opt-in) | Passed 2026-09-30 against Codex 0.159.2 (ChatGPT) and Claude Code 2.1.285 (Claude.ai/Max). Child process removes OPENAI_API_KEY, CODEX_API_KEY, ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN, retains provider-owned OAuth storage, checks subscription auth modes, then traverses ShellDeck create_client → complete → exact connection-test response for both. Sends two minimal real completions; normal CI never runs it. |
| SDTEST-1339 | `ai.rs::api_payloads_keep_guardrails_outside_untrusted_input_and_disable_storage` | SDUC-415 | Green | Pins OpenAI `instructions` + `store=false` and Anthropic `system`, separate from untrusted user context. |
| SDTEST-1340 | `ai.rs::configured_cli_requires_an_executable_file` | SDUC-413 | Green | A present but non-executable custom CLI path cannot be reported as available. |
| SDTEST-1342 | `app_config.rs::ai_config_round_trips_without_any_credential_field` | SDUC-413, SDUC-415 | Green | Pins backward-compatible `[ai]` persistence while proving API credentials have no serializable config field. |
diff --git a/docs/testing/tests-ui-and-app.md b/docs/testing/tests-ui-and-app.md
index 3e0134ff..6da761a1 100644
--- a/docs/testing/tests-ui-and-app.md
+++ b/docs/testing/tests-ui-and-app.md
@@ -660,3 +660,4 @@ observed defect. Evidence and repeatable journeys live in
| SDTEST-1932 | *to write* — workspace and agent inherited theme text | SDUC-475, SDUC-489 | **Red / P1** | Manual X11 screenshots reproduce black inherited text on dark backgrounds, then verify Workspaces onboarding and Agents heading/session/provider labels in Tokyo Night and Light. Automated rendered contrast coverage is still missing. |
| SDTEST-1933 | *to write* — filtered combobox row identity across outside-click dismissal | SDUC-314 | **Red / P0** | SDPATCH-046. Native X11 reproduction: click the sole filtered row, then verify its displayed selected label and the saved connection UUID before executing. Outside-click clears the filter before the row handler; selection must keep the original rendered identity. Keyboard selection must choose the same target. Automated GPUI event-order coverage is still missing. |
| SDTEST-1934 | *to write* — default secret masking, reveal lifecycle and API-key viewport | SDUC-310, SDUC-315 | **Red / P1** | Native X11 pass: freshly typed login password and API key are masked; eye toggles reveal/hide; login 401 keeps the secret masked and reopening starts masked; API-key value/reveal state survives resize at 1200 × 850, 820 × 600 and 580 × 600. Save/Delete remain reachable below the full-width input. No API key was stored and no provider request was sent. Automated native coverage remains missing. |
+| SDTEST-1936 | *to write* — subscription setup visibility, copy command and target-host guidance | SDUC-413, SDUC-475 | **Red / P1** | Manual X11 pass: both CLI choices are fully labeled, show setup without API-key controls, and copy the exact sign-in command. Shared setup is visible in Agents and remains reachable at 1200 × 850 and 580 × 600. The SSH-host instruction branch was source-reviewed; remote login was not exercised. Opening these surfaces does not launch login or inference. Automated rendered/click coverage remains missing. See subscription-qa-2026-09-30.md. |
diff --git a/docs/testing/visual-qa-2026-09-30.md b/docs/testing/visual-qa-2026-09-30.md
index 2d508c5d..4fc3f96d 100644
--- a/docs/testing/visual-qa-2026-09-30.md
+++ b/docs/testing/visual-qa-2026-09-30.md
@@ -57,8 +57,9 @@ SDTEST-1926..1929 cover recovery and subsequent observable behavior.
## Automated validation and remaining gaps
`cargo test --workspace --no-fail-fast` passes 853 unique tests: app 100,
-core 403, SSH 46, terminal 88, UI 203, update 13. Six live/infrastructure tests
-and two documentation examples remain ignored. `cargo clippy --workspace
+core 403, SSH 46, terminal 88, UI 203, update 13. Seven live/infrastructure tests
+and two documentation examples remain ignored after the subscription follow-up;
+[the subscription smoke](subscription-qa-2026-09-30.md) passed separately. `cargo clippy --workspace
--no-deps -- -D warnings`, formatting, Linux debug build and whitespace checks
pass. Vendored dependency dead-code warnings are unchanged.