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.