From 34e7336fdbeac5a33cee7fe659bc19cbc2b97ee0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 16:08:52 +0000 Subject: [PATCH 1/8] docs(tui): add computer fail-closed lock_v2 scenes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add designer and runtime lock boards for This PC/SSH fail-closed messaging and the Computer · Cloud shipped default at 40×12 and 120×40. Permission-prompt, cloud-handoff, and error-unavailable boards are unchanged. Co-authored-by: Mathis --- .../120x40/computer-cloud-default.png | Bin 0 -> 36627 bytes .../120x40/computer-disconnected.png | Bin 0 -> 36241 bytes .../40x12/computer-cloud-default.png | Bin 0 -> 20983 bytes .../40x12/computer-disconnected.png | Bin 0 -> 22894 bytes docs/media/tui-lock-v2/SPEC.md | 6 +- docs/media/tui-lock-v2/index.md | 4 +- docs/media/tui-lock-v2/runtime/README.md | 3 +- docs/media/tui-lock-v2/tools/boards.py | 51 ++++ .../txt/120x40/computer-cloud-default.txt | 40 +++ .../txt/120x40/computer-disconnected.txt | 40 +++ .../txt/40x12/computer-cloud-default.txt | 12 + .../txt/40x12/computer-disconnected.txt | 12 + scripts/render-tui-lock-v2.sh | 7 +- src/cortex-tui/src/app/state.rs | 6 + src/cortex-tui/src/lib.rs | 1 + src/cortex-tui/src/lock_v2.rs | 4 +- src/cortex-tui/src/lock_v2_boards.rs | 10 +- src/cortex-tui/src/lock_v2_computer.rs | 286 ++++++++++++++++++ src/cortex-tui/src/lock_v2_goal.rs | 2 + src/cortex-tui/src/lock_v2_ids.rs | 14 +- src/cortex-tui/src/ui/consts.rs | 7 + .../src/views/minimal_session/rendering.rs | 1 - .../src/views/minimal_session/text_utils.rs | 2 + .../src/views/minimal_session/view.rs | 43 ++- 24 files changed, 532 insertions(+), 19 deletions(-) create mode 100644 docs/media/tui-lock-v2/120x40/computer-cloud-default.png create mode 100644 docs/media/tui-lock-v2/120x40/computer-disconnected.png create mode 100644 docs/media/tui-lock-v2/40x12/computer-cloud-default.png create mode 100644 docs/media/tui-lock-v2/40x12/computer-disconnected.png create mode 100644 docs/media/tui-lock-v2/txt/120x40/computer-cloud-default.txt create mode 100644 docs/media/tui-lock-v2/txt/120x40/computer-disconnected.txt create mode 100644 docs/media/tui-lock-v2/txt/40x12/computer-cloud-default.txt create mode 100644 docs/media/tui-lock-v2/txt/40x12/computer-disconnected.txt create mode 100644 src/cortex-tui/src/lock_v2_computer.rs diff --git a/docs/media/tui-lock-v2/120x40/computer-cloud-default.png b/docs/media/tui-lock-v2/120x40/computer-cloud-default.png new file mode 100644 index 0000000000000000000000000000000000000000..85c9531de9f361fb471a1ef4c0d603b28937e423 GIT binary patch literal 36627 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 z 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 SHu4fN3Nc~nr&)qJZ~hm>TFRyX literal 0 HcmV?d00001 diff --git a/docs/media/tui-lock-v2/120x40/computer-disconnected.png b/docs/media/tui-lock-v2/120x40/computer-disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..e35e5e0a770f7540606992b585837a6fef80b53c GIT binary patch literal 36241 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�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/.txt` — the exact character grid of every board (diff a `MockTerminal` capture against these). - Renderer: `tools/render_lock_v2.py` + `tools/boards.py` (Python 3 + Pillow, IBM Plex Mono @@ -413,7 +413,7 @@ Narrow: no bars, `used / total pct%`. | model-effort-low·medium·high / model-list-hover | `model-list`, `model-list-hover`, `model-effort-high`, `model-effort-medium`, `model-effort-low`, `model-effort-hover` | | settings-appearance / mouse / row-hover / search / theme-submenu | `settings-appearance`, `settings-mouse`, `settings-row-hover`, `settings-search`, `settings-theme-submenu` | | mode chips (Shift+Tab) | `mode-agent`, `mode-plan`, `mode-ask`, `mode-bash` | -| permissions / mcp / plugins / usage-quota / sandbox / cloud | `permission-prompt`, `permission-prompt-hover`, `permissions-picker`, `mcp-servers`, `mcp-drop`, `plugins`, `usage`, `quota-exhausted`, `sandbox`, `sandbox-deny`, `cloud-handoff` | +| permissions / mcp / plugins / usage-quota / sandbox / cloud | `permission-prompt`, `permission-prompt-hover`, `permissions-picker`, `mcp-servers`, `mcp-drop`, `plugins`, `usage`, `quota-exhausted`, `sandbox`, `sandbox-deny`, `cloud-handoff`, `computer-disconnected`, `computer-cloud-default` | | diagnostics red/amber · interrupt/stopped | `diagnostics`, `interrupt-stopped`, `error-unavailable`, `offline`, `rate-limit` | | markdown table · diff hunk · code fence | `md-table`, `diff-hunk`, `edit-collapsed`, `code-fence`, `tool-tiles`, `tool-tiles-collapsed`, `shell-running` | | login / first-run | `login`, `login-waiting`, `login-success`, `login-error`, `first-run-tips` | @@ -426,7 +426,7 @@ Narrow (40×12) set: `welcome-cortex`, `welcome-agent`, `first-run-tips`, `sessi `tokens-topright`, `compact-chat`, `slash-palette`, `goal-chip-active`, `goal-chip-paused`, `goal-chip-done`, `goal-chip-budget`, `goal-chip-blocked`, `slash-model-typed`, `model-list`, `model-effort-high`, `settings-appearance`, `settings-mouse`, `settings-row-hover`, `settings-theme-submenu`, `mode-plan`, `mode-ask`, -`permission-prompt`, `mcp-servers`, `usage`, `diagnostics`, `interrupt-stopped`, `offline`, `rate-limit`, `diff-hunk`, `login`, `shortcuts-overlay`, +`permission-prompt`, `mcp-servers`, `usage`, `diagnostics`, `interrupt-stopped`, `offline`, `rate-limit`, `computer-disconnected`, `computer-cloud-default`, `diff-hunk`, `login`, `shortcuts-overlay`, `consent-local-tools`, `composer-file-chip`, `undo-sheet`. --- diff --git a/docs/media/tui-lock-v2/index.md b/docs/media/tui-lock-v2/index.md index cff79b57..8b8de734 100644 --- a/docs/media/tui-lock-v2/index.md +++ b/docs/media/tui-lock-v2/index.md @@ -90,6 +90,8 @@ Regenerate: `python3 tools/render_lock_v2.py --index` (fetches IBM Plex Mono on | `error-unavailable` | API down — product-facing error | [120x40](120x40/error-unavailable.png) | — | | `offline` | Network unreachable — held composer, distinct from API-down | [120x40](120x40/offline.png) | [40x12](40x12/offline.png) | | `rate-limit` | HTTP 429 rate limited — retry-after, distinct from quota | [120x40](120x40/rate-limit.png) | [40x12](40x12/rate-limit.png) | +| `computer-disconnected` | This PC/SSH fail-closed — no connected Code session | [120x40](120x40/computer-disconnected.png) | [40x12](40x12/computer-disconnected.png) | +| `computer-cloud-default` | Computer · Cloud shipped default (welcome info card) | [120x40](120x40/computer-cloud-default.png) | [40x12](40x12/computer-cloud-default.png) | | `tool-tiles` | Grouped tool calls expanded — Read / Grep / Shell | [120x40](120x40/tool-tiles.png) | — | | `tool-tiles-collapsed` | Grouped tool calls collapsed | [120x40](120x40/tool-tiles-collapsed.png) | — | | `shell-running` | Live Shell tile with output | [120x40](120x40/shell-running.png) | — | @@ -118,4 +120,4 @@ Regenerate: `python3 tools/render_lock_v2.py --index` (fetches IBM Plex Mono on | `composer-file-chip` | Composer with attached @file chip in prompt | [120x40](120x40/composer-file-chip.png) | [40x12](40x12/composer-file-chip.png) | | `undo-sheet` | /undo /redo /rewind sheet | [120x40](120x40/undo-sheet.png) | [40x12](40x12/undo-sheet.png) | -**87** boards at 120x40 · **41** at 40x12 · 128 PNGs. +**89** boards at 120x40 · **43** at 40x12 · 132 PNGs. diff --git a/docs/media/tui-lock-v2/runtime/README.md b/docs/media/tui-lock-v2/runtime/README.md index a45e0eeb..19f5575e 100644 --- a/docs/media/tui-lock-v2/runtime/README.md +++ b/docs/media/tui-lock-v2/runtime/README.md @@ -8,7 +8,8 @@ is banner green `#1F4945`; historical violet `#A78BFA` is not the lock. Designer boards (pixel target) live in `docs/media/tui-lock-v2/{40x12,120x40}/`. These runtime frames are what Designer cli signs off against. -SPEC §7: **87** boards at 120×40 and **41** at 40×12. Each filename is one +SPEC §7: **89** boards at 120×40 and **43** at 40×12. Each filename is one distinct live state — no two PNGs share a sha256. Includes `/goal` composer chips (`goal-chip-*`), distinct `offline` and `rate-limit` diagnostics, +Computer lock boards (`computer-disconnected`, `computer-cloud-default`), local-tools consent, composer `@file` chip, and `/undo` `/redo` `/rewind` sheet. diff --git a/docs/media/tui-lock-v2/tools/boards.py b/docs/media/tui-lock-v2/tools/boards.py index 2e03c2c6..9815d9e0 100644 --- a/docs/media/tui-lock-v2/tools/boards.py +++ b/docs/media/tui-lock-v2/tools/boards.py @@ -1412,6 +1412,55 @@ def board_rate_limit(s, c): footer(s, c, [("Enter", "retry"), ("/usage", "details")]) +# Exact engine constant (`src/cortex-engine/src/client/computer.rs`). +DISCONNECTED_RUNTIME = ( + "This PC and SSH Code execution require an already connected Code session. " + "Connect a host and resume that session, or leave CORTEX_COMPUTER unset to use Cloud. " + "No runtime was substituted." +) +DISCONNECTED_RUNTIME_NARROW = ( + "This PC and SSH need a connected Code session. No runtime was substituted." +) + + +def board_computer_disconnected(s, c): + """This PC/SSH fail-closed — no connected Code session; no Cloud substitute.""" + header(s, c) + placeholder = ( + "Connect a host — or unset for Cloud" + if c.narrow + else "Connect a host — or unset CORTEX_COMPUTER for Cloud" + ) + top = composer(s, c, placeholder=placeholder, focused=False, caret=False) + f = Flow(s, c, y=1 if c.narrow else 2, limit=top if c.narrow else top - 1) + f.user("run the local cargo tests" if not c.narrow else "run local tests", "09:18 AM") + f.line([("×", S_ERR), (" This PC disconnected", S_ERR)]) + body = DISCONNECTED_RUNTIME_NARROW if c.narrow else DISCONNECTED_RUNTIME + width = max(8, f.x1 - 1 - f.tx - 2) + for line in textwrap.wrap(body, width=width): + f.dim(line, indent=2) + footer(s, c, [("Enter", "retry"), ("Ctrl+x", "shortcuts")]) + + +def board_computer_cloud_default(s, c): + """Welcome frame with Computer · Cloud as the shipped default (not a handoff).""" + header(s, c, "0 / 500K") + title = [("Welcome to ", S), ("Cortex", S_BOLD)] + ( + [] if c.narrow else [(", the coding agent CLI", S_DIM)] + ) + welcome_lines(s, c, title) + y = 4 + if c.narrow: + s.spans(c.x0 + 2, y, [("Computer", S_DIM), (" ", S), ("Cloud", S)], max_x=c.x1) + else: + s.spans(c.x0 + 2, y, [("Directory", S_DIM), (" ", S), ("~/cortex", S)], max_x=c.x1) + s.spans(c.x0 + 2, y + 1, [("Org", S_DIM), (" ", S), ("Personal", S)], max_x=c.x1) + s.spans(c.x0 + 2, y + 2, [("Plan", S_DIM), (" ", S), ("Pro", S)], max_x=c.x1) + s.spans(c.x0 + 2, y + 3, [("Computer", S_DIM), (" ", S), ("Cloud", S)], max_x=c.x1) + composer(s, c) + footer(s, c) + + def board_tool_tiles(s, c): header(s, c) top = composer(s, c) @@ -1960,6 +2009,8 @@ def board_undo_sheet(s, c): ("error-unavailable", board_error_unavailable, False, "E", "API down — product-facing error"), ("offline", board_offline, True, "E", "Network unreachable — held composer, distinct from API-down"), ("rate-limit", board_rate_limit, True, "E", "HTTP 429 rate limited — retry-after, distinct from quota"), + ("computer-disconnected", board_computer_disconnected, True, "E", "This PC/SSH fail-closed — no connected Code session"), + ("computer-cloud-default", board_computer_cloud_default, True, "E", "Computer · Cloud shipped default (welcome info card)"), ("tool-tiles", board_tool_tiles, False, "E", "Grouped tool calls expanded — Read / Grep / Shell"), ("tool-tiles-collapsed", board_tool_tiles_collapsed, False, "E", "Grouped tool calls collapsed"), ("shell-running", board_shell_running, False, "E", "Live Shell tile with output"), diff --git a/docs/media/tui-lock-v2/txt/120x40/computer-cloud-default.txt b/docs/media/tui-lock-v2/txt/120x40/computer-cloud-default.txt new file mode 100644 index 00000000..9ab91368 --- /dev/null +++ b/docs/media/tui-lock-v2/txt/120x40/computer-cloud-default.txt @@ -0,0 +1,40 @@ + 0 / 500K + + Welcome to Cortex, the coding agent CLI + v0.1.7 · / commands · @ files · ! shell · & cloud + Directory ~/cortex + Org Personal + Plan Pro + Computer Cloud + + + + + + + + + + + + + + + + + + + + + + + + + + + + ╭─ Agent ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ > Plan, search, build anything │ + ╰─────────────────────────────────────────────────────────────────────────────────────────── Cortex Mini 1 (medium) ─╯ + + Shift+Tab:mode | Ctrl+x:shortcuts diff --git a/docs/media/tui-lock-v2/txt/120x40/computer-disconnected.txt b/docs/media/tui-lock-v2/txt/120x40/computer-disconnected.txt new file mode 100644 index 00000000..a7cc3adc --- /dev/null +++ b/docs/media/tui-lock-v2/txt/120x40/computer-disconnected.txt @@ -0,0 +1,40 @@ + 14K / 500K + + > run the local cargo tests 09:18 AM + + × This PC disconnected + This PC and SSH Code execution require an already connected Code session. Connect a host and resume that session, + or leave CORTEX_COMPUTER unset to use Cloud. No runtime was substituted. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ╭─ Agent ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ > Connect a host — or unset CORTEX_COMPUTER for Cloud │ + ╰─────────────────────────────────────────────────────────────────────────────────────────── Cortex Mini 1 (medium) ─╯ + + Enter:retry | Ctrl+x:shortcuts diff --git a/docs/media/tui-lock-v2/txt/40x12/computer-cloud-default.txt b/docs/media/tui-lock-v2/txt/40x12/computer-cloud-default.txt new file mode 100644 index 00000000..b03834bc --- /dev/null +++ b/docs/media/tui-lock-v2/txt/40x12/computer-cloud-default.txt @@ -0,0 +1,12 @@ + 0 / 500K + + Welcome to Cortex + v0.1.7 · / commands · @ files + Computer Cloud + + + ╭─ Agent ────────────────────────────╮ + │ > Plan, search, build anything │ + ╰─────────── Cortex Mini 1 (medium) ─╯ + + Shift+Tab:mode | Ctrl+x:shortcuts diff --git a/docs/media/tui-lock-v2/txt/40x12/computer-disconnected.txt b/docs/media/tui-lock-v2/txt/40x12/computer-disconnected.txt new file mode 100644 index 00000000..f4898f66 --- /dev/null +++ b/docs/media/tui-lock-v2/txt/40x12/computer-disconnected.txt @@ -0,0 +1,12 @@ + 14K / 500K + > run local tests + + × This PC disconnected + This PC and SSH need a connected + Code session. No runtime was + substituted. + ╭─ Agent ────────────────────────────╮ + │ > Connect a host — or unset for C… │ + ╰─────────── Cortex Mini 1 (medium) ─╯ + + Enter:retry | Ctrl+x:shortcuts diff --git a/scripts/render-tui-lock-v2.sh b/scripts/render-tui-lock-v2.sh index 578c284f..15cbf954 100755 --- a/scripts/render-tui-lock-v2.sh +++ b/scripts/render-tui-lock-v2.sh @@ -59,8 +59,8 @@ for spec in 40x12 120x40; do python3 scripts/ansi-frames-to-gif.py --frames "$frames" --png-only "$pngs" done -unique_pngs "$output_dir/40x12" 41 -unique_pngs "$output_dir/120x40" 87 +unique_pngs "$output_dir/40x12" 43 +unique_pngs "$output_dir/120x40" 89 python3 - "$output_dir" <<'PY' from pathlib import Path @@ -100,9 +100,10 @@ is banner green `#1F4945`; historical violet `#A78BFA` is not the lock. Designer boards (pixel target) live in `docs/media/tui-lock-v2/{40x12,120x40}/`. These runtime frames are what Designer cli signs off against. -SPEC §7: **87** boards at 120×40 and **41** at 40×12. Each filename is one +SPEC §7: **89** boards at 120×40 and **43** at 40×12. Each filename is one distinct live state — no two PNGs share a sha256. Includes `/goal` composer chips (`goal-chip-*`), distinct `offline` and `rate-limit` diagnostics, +Computer lock boards (`computer-disconnected`, `computer-cloud-default`), local-tools consent, composer `@file` chip, and `/undo` `/redo` `/rewind` sheet. EOF diff --git a/src/cortex-tui/src/app/state.rs b/src/cortex-tui/src/app/state.rs index a9a60d62..6109dd4c 100644 --- a/src/cortex-tui/src/app/state.rs +++ b/src/cortex-tui/src/app/state.rs @@ -206,6 +206,10 @@ pub struct AppState { pub offline_held: bool, /// HTTP 429 rate limit — follow-ups stay in the composer until Retry-After. pub rate_limit_held: bool, + /// This PC / SSH fail-closed — no host session; composer held, no Cloud substitute. + pub computer_held: bool, + /// Welcome info card should lock-paint Computer · Cloud as the shipped default. + pub show_computer_default: bool, /// Launched via the `agent` binary / alias. pub agent_entrypoint: bool, /// Token counter used / window. @@ -348,6 +352,8 @@ impl AppState { quota_held: false, offline_held: false, rate_limit_held: false, + computer_held: false, + show_computer_default: false, agent_entrypoint: false, tokens_used: 0, context_window: 500_000, diff --git a/src/cortex-tui/src/lib.rs b/src/cortex-tui/src/lib.rs index 69ae0232..7027bd1b 100644 --- a/src/cortex-tui/src/lib.rs +++ b/src/cortex-tui/src/lib.rs @@ -105,6 +105,7 @@ pub mod lock_palette; pub mod lock_proof; pub mod lock_v2; mod lock_v2_boards; +mod lock_v2_computer; mod lock_v2_goal; mod lock_v2_ids; mod lock_v2_network; diff --git a/src/cortex-tui/src/lock_v2.rs b/src/cortex-tui/src/lock_v2.rs index 9d5061cf..9df6dae0 100644 --- a/src/cortex-tui/src/lock_v2.rs +++ b/src/cortex-tui/src/lock_v2.rs @@ -158,8 +158,8 @@ mod tests { #[test] fn lock_v2_wide_count_is_spec() { - assert_eq!(LOCK_V2_WIDE_IDS.len(), 87); - assert_eq!(LOCK_V2_NARROW_IDS.len(), 41); + assert_eq!(LOCK_V2_WIDE_IDS.len(), 89); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 43); } #[test] diff --git a/src/cortex-tui/src/lock_v2_boards.rs b/src/cortex-tui/src/lock_v2_boards.rs index df07d65f..8e616bd8 100644 --- a/src/cortex-tui/src/lock_v2_boards.rs +++ b/src/cortex-tui/src/lock_v2_boards.rs @@ -14,7 +14,9 @@ use crate::interactive::builders::{ build_permissions_picker, build_plan_confirm, build_question_prompt, build_sandbox_deny_prompt, }; use crate::lock_v2::PRODUCT_ERROR; -use crate::lock_v2_goal::{apply_goal_chip_scene, show_goal_in_narrow_palette}; +use crate::lock_v2_goal::{ + apply_computer_scene, apply_goal_chip_scene, show_goal_in_narrow_palette, +}; use crate::lock_v2_network::apply_offline_rate_limit_scene; use crate::lock_v2_parity::apply_parity_scene; use crate::lock_v2_scenes::*; @@ -811,6 +813,12 @@ Tell me what you'd like to do.", id if apply_offline_rate_limit_scene(id, &mut state) => {} id if apply_parity_scene(id, &mut state, width) => {} id if apply_goal_chip_scene(id, &mut state) => {} + "computer-disconnected" | "computer-cloud-default" => { + assert!( + apply_computer_scene(id, &mut state, width), + "computer lock scene {id}" + ); + } other => panic!("unknown lock v2 scene {other}"), } state diff --git a/src/cortex-tui/src/lock_v2_computer.rs b/src/cortex-tui/src/lock_v2_computer.rs new file mode 100644 index 00000000..bc40bd6d --- /dev/null +++ b/src/cortex-tui/src/lock_v2_computer.rs @@ -0,0 +1,286 @@ +//! Computer lock v2 scenes — This PC/SSH fail-closed and Cloud default. +//! +//! Split out of [`crate::lock_v2`] so adding these boards does not grow that +//! file past the source-policy line-count baseline. + +use cortex_core::widgets::Message; +use cortex_engine::client::{ComputerKind, DISCONNECTED_RUNTIME}; + +use crate::app::AppState; +use crate::ui::consts::{DISCONNECTED_TITLE, PLACEHOLDER_DISCONNECTED}; + +/// Computer lock boards. Each filename is one live state. +pub const COMPUTER_SCENE_IDS: &[&str] = &["computer-disconnected", "computer-cloud-default"]; + +/// Narrow (40-column) fail-closed body — still names This PC/SSH and refuses +/// a substitute. Wide boards use the engine constant verbatim. +pub const DISCONNECTED_RUNTIME_NARROW: &str = + "This PC and SSH need a connected Code session. No runtime was substituted."; + +/// Apply a Computer lock scene. Returns `false` when `id` is not one of these. +pub fn apply_computer_scene(id: &str, state: &mut AppState, width: u16) -> bool { + match id { + "computer-disconnected" => { + seed_disconnected(state, width); + true + } + "computer-cloud-default" => { + seed_cloud_default(state); + true + } + _ => false, + } +} + +fn seed_disconnected(state: &mut AppState, width: u16) { + state.show_launch_splash = false; + state.tokens_used = 14_000; + state.computer_held = true; + state.caret_visible = false; + state.add_message( + Message::user(if width <= 40 { + "run local tests" + } else { + "run the local cargo tests" + }) + .with_timestamp("09:18 AM"), + ); + state.add_message(Message::system(format!("× {DISCONNECTED_TITLE}"))); + state.add_message(Message::system(if width <= 40 { + DISCONNECTED_RUNTIME_NARROW + } else { + DISCONNECTED_RUNTIME + })); +} + +fn seed_cloud_default(state: &mut AppState) { + state.show_launch_splash = true; + state.tokens_used = 0; + state.show_computer_default = true; +} + +/// Squeezed transcript text so wrapped rows still match engine copy. +/// Scrollbar and box-drawing cells become spaces before collapse. +pub fn squeezed_plain(plain: &str) -> String { + plain + .chars() + .map(|c| { + if c.is_ascii_alphanumeric() || c.is_ascii_punctuation() || c.is_whitespace() { + c + } else { + ' ' + } + }) + .collect::() + .split_whitespace() + .collect::>() + .join(" ") +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lock_v2::render_lock_v2_scene; + use crate::lock_v2_ids::{LOCK_V2_NARROW_IDS, LOCK_V2_WIDE_IDS}; + use cortex_core::style::{ERROR, TEXT_DIM}; + + const SIZES: [(u16, u16); 2] = [(120, 40), (40, 12)]; + + fn has_fg( + frame: &crate::lock_proof::LockFrame, + needle: &str, + color: ratatui::style::Color, + ) -> bool { + let width = frame.buffer.area.width; + let height = frame.buffer.area.height; + for y in 0..height { + let row: String = (0..width) + .map(|x| frame.buffer[(x, y)].symbol().to_string()) + .collect(); + if let Some(at) = row.find(needle) { + let x = at as u16; + if frame.buffer[(x, y)].fg == color { + return true; + } + } + } + false + } + + #[test] + fn computer_scene_ids_are_registered() { + assert_eq!(COMPUTER_SCENE_IDS.len(), 2); + for id in COMPUTER_SCENE_IDS { + assert!(LOCK_V2_WIDE_IDS.contains(id), "{id} missing from wide"); + assert!(LOCK_V2_NARROW_IDS.contains(id), "{id} missing from narrow"); + } + assert_eq!(LOCK_V2_WIDE_IDS.len(), 89); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 43); + let mut seen = std::collections::HashSet::new(); + for id in LOCK_V2_WIDE_IDS.iter().chain(LOCK_V2_NARROW_IDS) { + seen.insert(*id); + } + assert!(seen.contains("computer-disconnected")); + assert!(seen.contains("computer-cloud-default")); + } + + #[test] + fn disconnected_uses_engine_copy_and_fail_closed() { + for (width, height) in SIZES { + let frame = + render_lock_v2_scene("computer-disconnected", width, height).expect(id_at(width)); + let plain = &frame.plain; + let squeezed = squeezed_plain(plain); + assert!( + plain.contains("This PC"), + "missing This PC at {width}x{height}:\n{plain}" + ); + assert!( + plain.contains("SSH"), + "missing SSH at {width}x{height}:\n{plain}" + ); + assert!( + plain.contains("No runtime was substituted"), + "missing refuse at {width}x{height}:\n{plain}" + ); + assert!( + plain.contains(DISCONNECTED_TITLE), + "missing title at {width}x{height}:\n{plain}" + ); + if width >= 120 { + assert!( + squeezed.contains(DISCONNECTED_RUNTIME), + "wide board must carry the engine constant:\n{plain}" + ); + } else { + assert!( + squeezed.contains(DISCONNECTED_RUNTIME_NARROW), + "narrow board must keep the connected-session gist:\n{plain}" + ); + } + assert!( + !plain.contains("temporarily unavailable"), + "must not reuse error-unavailable:\n{plain}" + ); + assert!( + !plain.contains("Handed off"), + "must not look like cloud-handoff:\n{plain}" + ); + assert!( + plain.contains("Enter") && plain.contains("retry"), + "held footer Enter:retry at {width}x{height}:\n{plain}" + ); + assert!( + !plain.contains("Shift+Tab"), + "held board must not show idle Shift+Tab:\n{plain}" + ); + assert!( + plain.contains("Connect a host"), + "held placeholder at {width}x{height}:\n{plain}" + ); + if width >= 120 { + assert!( + plain.contains(PLACEHOLDER_DISCONNECTED) + || squeezed.contains("CORTEX_COMPUTER"), + "wide placeholder:\n{plain}" + ); + } else { + assert!( + squeezed.contains("Connect a host") && squeezed.contains("unset for"), + "narrow placeholder:\n{plain}" + ); + } + } + } + + fn id_at(width: u16) -> &'static str { + if width <= 40 { + "computer-disconnected-narrow" + } else { + "computer-disconnected-wide" + } + } + + #[test] + fn disconnected_title_is_error_red_and_body_is_dim() { + for (width, height) in SIZES { + let frame = render_lock_v2_scene("computer-disconnected", width, height).expect("disc"); + assert!( + has_fg(&frame, "This PC disconnected", ERROR) || has_fg(&frame, "×", ERROR), + "title must be error-red at {width}x{height}" + ); + assert!( + has_fg(&frame, "No runtime", TEXT_DIM) + || has_fg(&frame, "substituted", TEXT_DIM) + || has_fg(&frame, "connected", TEXT_DIM), + "body must be dim at {width}x{height}:\n{}", + frame.plain + ); + } + } + + #[test] + fn disconnected_is_not_unavailable_or_handoff() { + let disc = render_lock_v2_scene("computer-disconnected", 120, 40).unwrap(); + let unavail = render_lock_v2_scene("error-unavailable", 120, 40).unwrap(); + let handoff = render_lock_v2_scene("cloud-handoff", 120, 40).unwrap(); + let quota = render_lock_v2_scene("quota-exhausted", 120, 40).unwrap(); + assert_ne!(disc.ansi, unavail.ansi); + assert_ne!(disc.ansi, handoff.ansi); + assert_ne!(disc.ansi, quota.ansi); + assert!(unavail.plain.contains("temporarily unavailable")); + assert!(!disc.plain.contains("temporarily unavailable")); + assert!(handoff.plain.contains("Handed off") || handoff.plain.contains("Cortex Cloud")); + assert!(!disc.plain.contains("Handed off")); + } + + #[test] + fn cloud_default_shows_computer_cloud_not_handoff() { + for (width, height) in SIZES { + let frame = render_lock_v2_scene("computer-cloud-default", width, height) + .expect("cloud-default"); + let plain = &frame.plain; + assert!( + plain.contains("Computer"), + "missing Computer at {width}x{height}:\n{plain}" + ); + assert!( + plain.contains(ComputerKind::Cloud.label()), + "missing Cloud label at {width}x{height}:\n{plain}" + ); + assert!( + plain.contains("Welcome") && plain.contains("Cortex"), + "welcome chrome at {width}x{height}:\n{plain}" + ); + assert!( + !plain.contains("Handed off"), + "must not reuse cloud-handoff:\n{plain}" + ); + assert!( + !plain.contains("This PC disconnected"), + "cloud default is not the fail-closed board:\n{plain}" + ); + assert!( + plain.contains("Shift+Tab"), + "idle footer at {width}x{height}:\n{plain}" + ); + } + let cloud = render_lock_v2_scene("computer-cloud-default", 120, 40).unwrap(); + let welcome = render_lock_v2_scene("welcome-cortex", 120, 40).unwrap(); + let handoff = render_lock_v2_scene("cloud-handoff", 120, 40).unwrap(); + assert_ne!(cloud.ansi, welcome.ansi); + assert_ne!(cloud.ansi, handoff.ansi); + assert!(!welcome.plain.contains("Computer")); + assert!(cloud.plain.contains("Directory") || cloud.plain.contains("Computer")); + } + + #[test] + fn computer_kind_labels_match_product() { + assert_eq!(ComputerKind::ThisPc.label(), "This PC"); + assert_eq!(ComputerKind::Cloud.label(), "Cloud"); + assert_eq!(ComputerKind::Ssh.label(), "SSH"); + assert!(DISCONNECTED_RUNTIME.contains("This PC")); + assert!(DISCONNECTED_RUNTIME.contains("SSH")); + assert!(DISCONNECTED_RUNTIME.contains("No runtime was substituted")); + } +} diff --git a/src/cortex-tui/src/lock_v2_goal.rs b/src/cortex-tui/src/lock_v2_goal.rs index 7b0a488c..53b58bd6 100644 --- a/src/cortex-tui/src/lock_v2_goal.rs +++ b/src/cortex-tui/src/lock_v2_goal.rs @@ -8,6 +8,8 @@ use cortex_engine::goal::{Goal, GoalState}; use crate::app::AppState; +pub use crate::lock_v2_computer::apply_computer_scene; + /// Composer chip lock boards. Each filename is one live state. pub const GOAL_CHIP_IDS: &[&str] = &[ "goal-chip-active", diff --git a/src/cortex-tui/src/lock_v2_ids.rs b/src/cortex-tui/src/lock_v2_ids.rs index 77ffa55d..d7e965f9 100644 --- a/src/cortex-tui/src/lock_v2_ids.rs +++ b/src/cortex-tui/src/lock_v2_ids.rs @@ -1,7 +1,7 @@ //! Lock v2 scene id lists. Split out of [`crate::lock_v2`] so adding `/goal` //! boards does not grow that file past the source-policy line-count baseline. -/// Narrow (40×12) SPEC §7 set — 41 boards. +/// Narrow (40×12) SPEC §7 set — 43 boards. pub const LOCK_V2_NARROW_IDS: &[&str] = &[ "welcome-cortex", "welcome-agent", @@ -38,6 +38,8 @@ pub const LOCK_V2_NARROW_IDS: &[&str] = &[ "interrupt-stopped", "offline", "rate-limit", + "computer-disconnected", + "computer-cloud-default", "diff-hunk", "login", "shortcuts-overlay", @@ -46,7 +48,7 @@ pub const LOCK_V2_NARROW_IDS: &[&str] = &[ "undo-sheet", ]; -/// Wide (120×40) SPEC §7 set — 87 boards. +/// Wide (120×40) SPEC §7 set — 89 boards. pub const LOCK_V2_WIDE_IDS: &[&str] = &[ "welcome-cortex", "welcome-agent", @@ -108,6 +110,8 @@ pub const LOCK_V2_WIDE_IDS: &[&str] = &[ "error-unavailable", "offline", "rate-limit", + "computer-disconnected", + "computer-cloud-default", "tool-tiles", "tool-tiles-collapsed", "shell-running", @@ -153,8 +157,8 @@ mod tests { #[test] fn lock_v2_id_counts_and_unique() { - assert_eq!(LOCK_V2_WIDE_IDS.len(), 87); - assert_eq!(LOCK_V2_NARROW_IDS.len(), 41); + assert_eq!(LOCK_V2_WIDE_IDS.len(), 89); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 43); let mut wide = HashSet::new(); for id in LOCK_V2_WIDE_IDS { assert!(wide.insert(*id), "duplicate wide id {id}"); @@ -167,5 +171,7 @@ mod tests { "narrow id {id} is not in LOCK_V2_WIDE_IDS" ); } + assert!(wide.contains("computer-disconnected")); + assert!(wide.contains("computer-cloud-default")); } } diff --git a/src/cortex-tui/src/ui/consts.rs b/src/cortex-tui/src/ui/consts.rs index 659f690f..3d98eab0 100644 --- a/src/cortex-tui/src/ui/consts.rs +++ b/src/cortex-tui/src/ui/consts.rs @@ -59,6 +59,13 @@ pub const PLACEHOLDER_RATE_LIMIT: &str = "Add a follow-up — held until the rat /// Narrow (40-column) form of the rate-limit placeholder. pub const PLACEHOLDER_RATE_LIMIT_NARROW: &str = "Held until the rate limit resets"; +/// This PC / SSH fail-closed title painted in error red (`× This PC disconnected`). +pub const DISCONNECTED_TITLE: &str = "This PC disconnected"; +/// Composer placeholder while This PC/SSH is fail-closed (no host session). +pub const PLACEHOLDER_DISCONNECTED: &str = "Connect a host — or unset CORTEX_COMPUTER for Cloud"; +/// Narrow (40-column) form of the disconnected placeholder. +pub const PLACEHOLDER_DISCONNECTED_NARROW: &str = "Connect a host — or unset for Cloud"; + /// Sandbox deny title painted in error red. pub const SANDBOX_DENIED_TITLE: &str = "Sandbox denied"; diff --git a/src/cortex-tui/src/views/minimal_session/rendering.rs b/src/cortex-tui/src/views/minimal_session/rendering.rs index adacb9fb..5c007839 100644 --- a/src/cortex-tui/src/views/minimal_session/rendering.rs +++ b/src/cortex-tui/src/views/minimal_session/rendering.rs @@ -1051,7 +1051,6 @@ pub fn render_update_banner( let x = area.x + 2; let y = area.y; - // Ensure we don't overflow if x + banner_width > area.right() { return; } diff --git a/src/cortex-tui/src/views/minimal_session/text_utils.rs b/src/cortex-tui/src/views/minimal_session/text_utils.rs index de821f16..22c4eafc 100644 --- a/src/cortex-tui/src/views/minimal_session/text_utils.rs +++ b/src/cortex-tui/src/views/minimal_session/text_utils.rs @@ -14,6 +14,7 @@ pub(super) fn system_message_is_error(content: &str) -> bool { "Invalid", "You're offline", "Rate limited", + "This PC disconnected", "context limit", "Stopped", "quota exhausted", @@ -174,6 +175,7 @@ mod tests { fn lock_red_system_copy_includes_offline_and_rate_limit() { assert!(system_message_is_error("× You're offline")); assert!(system_message_is_error("× Rate limited")); + assert!(system_message_is_error("× This PC disconnected")); assert!(system_message_is_error( "The coding service is temporarily unavailable" )); diff --git a/src/cortex-tui/src/views/minimal_session/view.rs b/src/cortex-tui/src/views/minimal_session/view.rs index 6096f7e4..6fd61b5a 100644 --- a/src/cortex-tui/src/views/minimal_session/view.rs +++ b/src/cortex-tui/src/views/minimal_session/view.rs @@ -3,7 +3,7 @@ use ratatui::buffer::Buffer; use ratatui::layout::Rect; use ratatui::style::{Modifier, Style}; -use ratatui::text::Line; +use ratatui::text::{Line, Span}; use ratatui::widgets::{Paragraph, Widget}; use super::rendering::{ @@ -162,6 +162,9 @@ impl<'a> MinimalSessionView<'a> { let mut all_lines: Vec> = Vec::new(); all_lines.extend(generate_welcome_lines(width, &self.colors, self.app_state)); + if self.app_state.show_computer_default { + all_lines.extend(computer_default_info_lines(width, &self.colors)); + } let message_lines = generate_message_lines(width, &self.colors, self.app_state); if !message_lines.is_empty() { @@ -261,7 +264,8 @@ impl<'a> MinimalSessionView<'a> { || self.app_state.has_pending_approval(); let focused = self.app_state.settings_modal.is_none() && !self.app_state.shortcuts_open - && !prompt_owns_focus; + && !prompt_owns_focus + && !self.app_state.computer_held; let effort = self .app_state .thinking_budget @@ -319,6 +323,12 @@ impl<'a> MinimalSessionView<'a> { } } else if self.app_state.quota_held { crate::ui::consts::PLACEHOLDER_QUOTA + } else if self.app_state.computer_held { + if area.width < 80 { + crate::ui::consts::PLACEHOLDER_DISCONNECTED_NARROW + } else { + crate::ui::consts::PLACEHOLDER_DISCONNECTED + } } else if self.is_task_running() { PLACEHOLDER_RUNNING } else if self.app_state.agent_entrypoint { @@ -731,7 +741,10 @@ pub const PALETTE_FOOTER_HINT_SHORT: &str = "Enter:send | Ctrl+x:shortcuts"; impl<'a> MinimalSessionView<'a> { fn footer_set(&self, is_task_running: bool, width: u16) -> FooterSet { - if self.app_state.offline_held || self.app_state.quota_held { + if self.app_state.offline_held + || self.app_state.quota_held + || self.app_state.computer_held + { return FooterSet::Unavailable; } if self.app_state.rate_limit_held { @@ -863,6 +876,30 @@ fn composer_has_completed_file_chip(text: &str) -> bool { false } +/// Welcome info-card rows that make Computer · Cloud lock-evident. +fn computer_default_info_lines(width: u16, colors: &AdaptiveColors) -> Vec> { + let dim = Style::default().fg(colors.text_dim); + let text = Style::default().fg(colors.text); + let indent = if width < 20 { 0 } else { 3 }; + let pad = " ".repeat(indent as usize); + let row = |label: &str, value: &str| { + Line::from(vec![ + Span::raw(pad.clone()), + Span::styled(format!("{label:<10}"), dim), + Span::styled(value.to_string(), text), + ]) + }; + let cloud = cortex_engine::client::ComputerKind::Cloud.label(); + let mut lines = vec![Line::from("")]; + if width >= 80 { + lines.push(row("Directory", "~/cortex")); + lines.push(row("Org", "Personal")); + lines.push(row("Plan", "Pro")); + } + lines.push(row("Computer", cloud)); + lines +} + fn composer_display_text(state: &AppState) -> String { if let Some(istate) = state.get_interactive_state() { let title = istate.title.to_ascii_lowercase(); From 5103a43bc9b8a8547db49a0955cb76b812ec7251 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 16:13:46 +0000 Subject: [PATCH 2/8] docs(tui): add runtime computer lock_v2 pngs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rasterise the new fail-closed and Cloud-default scenes from MockTerminal at 40×12 and 120×40, and quiet non-test imports in the computer lock module. Co-authored-by: Mathis --- .../runtime/120x40/computer-cloud-default.png | Bin 0 -> 8406 bytes .../runtime/120x40/computer-disconnected.png | Bin 0 -> 9233 bytes .../runtime/40x12/computer-cloud-default.png | Bin 0 -> 3823 bytes .../runtime/40x12/computer-disconnected.png | Bin 0 -> 4537 bytes src/cortex-tui/src/lock_v2_computer.rs | 25 +++++++++--------- 5 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 docs/media/tui-lock-v2/runtime/120x40/computer-cloud-default.png create mode 100644 docs/media/tui-lock-v2/runtime/120x40/computer-disconnected.png create mode 100644 docs/media/tui-lock-v2/runtime/40x12/computer-cloud-default.png create mode 100644 docs/media/tui-lock-v2/runtime/40x12/computer-disconnected.png diff --git a/docs/media/tui-lock-v2/runtime/120x40/computer-cloud-default.png b/docs/media/tui-lock-v2/runtime/120x40/computer-cloud-default.png new file mode 100644 index 0000000000000000000000000000000000000000..dab3ebac894d55c81caf321ed95c2175ecd363dc GIT binary patch literal 8406 zcmeHMX*|^J*Z-+#v9!pNwMasCjghTxdzL~&cIjs9gRwJ+q7srVjHNJ&kQhsLLbkE* z+gOJ(){w^XT>3xH=kvU|pI86q|ElZ7%_PY>tF-1f9);(`5dA!PJ_A?XI)m-{ zDclk3Z}pFIht8QcwOuJW8lbrmeeuLO=%gEA2ln#k(O{x_Tdv>931=Yj-!Zyyr5Un7 z(5m2sBG)*JMOxI+io)a-H7_ui?tw`kYJ0}YxShh~UW}?VjK+`WbeAr2YHsH_qQL?d zM!H=FNB%7Qr{)o@P)1;#Mgp{CRlXh7tYBRjuJ&x(zOWOK>u(LGf}r@g=gqB69%};( zfqM69PeRbk%h@^}8`Ue^QP^+8Uq>Fa#r>tvhX-QYe&zHXjS5Is4qY?n93>^w@k7wf z=U5KQI_2rBzK)fd2EX@eaXFGY&M=rP>wKu0EH?6RT`kwZJUdI;#I<)lkB(svg}K@6 z2@HmNL~!rJbbgE12wfX>j#3j*D88`LZ_iwVA&=&N;_)@ zPFp8q6w)&IwkcKKZCNANteh_kMYtH%Mg3Y08TUR2AM($_iSM|xhIG(`>=?4zLqQmS zpus)y2pEaEewaE$)$Rcl#O$y5`Ka}e3n#r2dABKMSRZper(E|2{I`VK1;a3^E#+gv% zPlJUg0~f3F3|-jmd{_|*X`PbmIN6cPI%Pq$+X5i?B zQ6Ed@@H5x|YAOhx5PI7_4BgY@#PB)Pt5_um5hj@BWj~2l<63V&NzKUk+E2%=0{$w@ zKi@4MRTYnp^wShlJ`NF?q|Uo6w@0j85>b2>-N^(&KiKHuy+tXRUlUW2CgDe&_cBgItO$l!G>Ye&w+J#*=<3QgGfcL8AJq>R@2Uz`~K&~%T7f9`w(2p zaQ2)MSB>05M>@#GC&C6k!@P2hw*aADL$UB{#`@GG50UUyIcJ?1+N?-!*^9!8X`S8G zi74eUIa7+(@(r?{f6i!ksQ@z8;-XlopHpJC^1eoS>HhL0Q;~4Hwe5Jd4F7 zo-B>TGiLqBv21%by-{b7Bn+EaUy#S5!$uYF#G$Ft3WkV`BqbZVob94BUVW}q(ET5t z?#GFuazTcsRhK*pA?T^BwTAE+!`(bZ*5w+WGrL-Tx>#xD)ccV^aw#2!$*EoHPi_r0maK*t*K{bwOmT4(pi%4 z#c3P<=?mTJXYKqaI|R+@Zfk6I!)K*>F!EKuNNgmna8h#7)|u-~K10SjXKzQ>2X*xnZ0+7>|~Age)b>QFrX%@YUpmkTc{D&H z_uA@nExbT)*588P$Z%6ykMKtO2{n|M)aHWQeIh$|GEMi&gJtnI#O2&Av;OtC%*ik& zj^}z_S&b$W7TW^C6Xfz5&MyZ)7z0PurgLoOzC>&)6!y{^m}SsV2PVu{5-)$cHOpAx z$=dldnkGmdpE|{1e-OIp?uMg1CE~cj7I&kAhMrPxlEy2!heKgMZTSx9JYqERPJGni z?=Fe_;MTON%u5F~`+|_mLCg|l7j0oW1`*FE3B9N8c_v2%*(TEQV|x}_#Hm1*`A-bf zR**=BruSxCE5@r8xSKs^lv6vA+l_kxiY14M?z^oJafzoTTGvu$%%=18MxU-Ti_0>z z)3QGtq;BoW$YrR1b5R?Dg5)tEXHa373pE9+1LZ<}8e`^JK|(gy4yPhvb?clm`(R}q z?jf=GjJdkt3H7#U^UYiw`{0QjHR}A{Vv$|W|6!-pj*R#q&P}gG- zeNR>@vm{E7JGxHIci<|%g!Is-c9Nv9v`t}aPuOp}FnVdAjEX^KWpTmLA*~)Ww`aLaui_zL#32{j4BrTlj#i*6HswaTtva%h}5{d?z8l#8l zNTx$u`)f~|-$rAkIYg?BDsf(M-aQhmlug?q;i~A*1s;KF-{bG=$!`CODVL7@@O&7i zWhh&EYqshd(#ObF?k%m(iQ>GqSWEApUlzq!6?8*Elx6jeqeF@vdC_^U*oiBhL$)$N z()vLaBUD;FcKzdILcpB`**{`qfc~1oEGT>0R{7)Y7Ep z@J4WJI%z}3%)4fCdqv}HMz74uGw@OsO3jNXitE0Tdn^Mm&>*E^CYLSXQ5Y-z^K7mM za|@BPpWA;g1hns>Wqv4()rUdae2iGXyiK`UCDY-#H8o}>EL11r4EDU?HdW@^r0R(bxYHx!LIja*e1QmBi#bZ z1HcfHM{7_K;|oCx{yw@S)u=TWcjtmp3#DN@Q?`N-1RpH~jg9z+N{)wzCGg5Tm~fdS z&ZJwZLy%=2`Z)I9^P}1QAE_S_zUY}i5OK+v4nC<^qWE}cTK~AVNB4M6SH5UQ)ss&t zdoZ_DKpRbgkCO{rA6l*MZ0?+K(s3g2h%Cd$ED@`TJy{(Qz#g)#Lr;1;QbN9Gn3l#) zg=_r-=l)4iDu^gC5xIO*7*nEu9uRt$r<&B*-dD!mSHRKJ9T9v_{ZD@fm=F30a_1@w zV780(1)a_Ah|<+K`Lxc(ddgmP;2p3axSDhPk#*hG*mTQ(2|q+Wz0;gQse|~}uUOn& zz@=@$@kFkC-zXHU#5!}F)-Sl1;8!ylCQh=dBfB4+D+9(EX(#mAs3wc@@ih#icsYTZ zOX-bt$hxkzy&3V_4;ivACgsbgG{rWAh#J{c+~C^(7W;(4m+G4ix1ONYvf zy19By5HWI?C9z5gr_a(pKmPJ?!V)P=gO#~8pQ~*n^a7lmB#+=%#)9G=XCUYn4*iS<-w!swrs=cie2waOD2DQ-w+>h{YiGG#3D-HdXy*A8j)IEDTv;82oB(EA9}26 zhuNuZoUX`8|M<2yqq}R=g@{J#e7QOTtUia|q(i<1f1>HBLJI|!Zk4SFM@2`^;>(3| zM3JzH$(7`d_y!D!Q8)sN%lu;}gi`f(l&x zJS=9LV<AFEW7@UKM4Fm;12?S5cq?@9|Zm&@c)LuP0mAphu@A;+in~rJv^9M z_CG8?HhzJwRLOVVLmtfLJ{<(8Zcu#6hfL4deh#KG8g%L&tQQ}R*c(yjLim8{?B*8_ z3GCV_lg(1_f-J?H0x1UUs*kOcI@6w+1Yi_CL+gM-YD+dr#x&r2$Fm*OjJl?_Y7X`( z+=08%Lr5SVmlEI=L3-|iY%m5 zz_-$$%1dCaJF40;(=@J%NcM3oFXOL*x6Wq!2;ZI+PKxGRL-42qYG+MeU7azh!g;-3 zzxwv{U6nYn=oUVsWFxLmHS_OVfK3iH+ugPY-|z%YYxtZbcpABM*rC43RAB=LW%4{` zW!tX~6gbe`QLyrWGI^jnP+f(0|p2Y2TCI*a{xQV%sUr4}aD7Cs<1Xi-+w} zr%caRl9$b6C5-3D0poeQMBUgGOA}=(1-| z9~M4a1eg82w1cv7=YyTW8X-J)yKhHUJv_ydYsI>t-~%X*B4lxyC7we*hfRw?`BB(7 zoU1AJ^I#N*G?HwUHY0pT`YP*aJ6=wZhm)z=W{aPnc^pRQtskFy)g4(^6QwnJ=L64# z%Vu^2Ft(ak)Ba$;GtfO6NuSs~uQ1)*VZ=VkSGMw`H^e%lL%Cz#%;Y=Ji>1V?b|)=x z&o6f98oQ@wj3;w>7Q;N=Uiqw9QFMH76#Jyta+iQZo2mf2F;|4%zAJjxG{d>5d-2Dl zP6dmjjH^_Sq9FjRL7We7)@~$PH7w+t7E5TaFlHBuuIPGk6RdWdNqvrHs@sypFoqs z8K7E+8}kL@qYAs)L&9Q8sqd{d8|Y8GVLRE!8-Y@o9CcCvtZ;`^b@qa4$dI?r)%4^l4i2ph(pD4`ItuE~> zym<@?a`4L?{1L^i=v+G5nWjkL>m;Gk4uO=>NgjvE3BgqL=Li8Dtv7z7qxl5-2 zl#`_DWX)-yN6TT|SxN+VNUaR1QYZ4Y_s>8pDae>XS&$3ZKZ!Qm-OU&c^n2N=~-oBLcmboF-M18*&h>8gfmnS8G?C7tZ2W)|VV ztx4Cb7DZosjVN+Tx296Z*fr>zkCt{&BAa{~UeCJwAt+f*f^+w+lgU29pdjWDeQk9E zHDQ;T!q~FXBE}|%;KAf}e7WbpNn1zJWxr;Gg2FYTkn@#+mzn%=rT&jDXrqQxIuj?~sTO6OzvIRbZkJ2r zz;u+5rWm-DU8v+L20@Mn-%|Zv+hIdvdq2!yU1K^~8Z5l+svaOEamsFbak$YuYsv5F zz7*-NGQ8oc!L&b%AHH=31iNPLz`tR7x5qU?F2krGZ_a1K>)4d`nG^^f>ieyaO~SnxRw z0Lr}KVxRb>^meuPZX)-AT9XXYQL>=TM>O|0dM~-q&L;wRDvn37`!}mj@0Kpgm*=%F zFprg5eA;>eJ6#`IBXBttHIJOh{2SZ?S{jM{wZJkE&dt-@5jZ}F&ucw3P*F`Cav*6w z{jZD2yFJ!!&scsY2Oo-e5kS(lPoGO80tfkzjG(9B?vm0xvtSOk8~jnFngLEh@oYd% zl){MU<ZI zRHF%96Dm4Aa-b|2M6(kenp!c&E{K{@ zseG2a$*lIU_?ADGm*t(s?@sO9ugF&GFuvYI(6p;{ZcXUh0z~6mpZy=47)znC49^ zf_ayH{n}hW9NP*P7EKj}pgscyA{`BlS&pQ2n|cvOKZ7s)9Idl-v805Ts!TUZPW?XJ zrO4Z!b1cH5<{3nOLAR%tQmjU=sy)kwN2pdzCO)D}cm07o9Y@@@H@R9r>kA8fi<>g2 zg71Du`|*e)Cv{~r_?|n03q$28I?`1kM9Dk1qTu0fRf6punV4Cojv??67D!!2T%4_4iT4Kym>nj~2<-yTOqf@Vl@S4Eg8e@-9+rG9l z3`gr9D54{i1FJ#aINU?*^5~h|c50?Y)I63;zUY(m-ivRIHfv#P-?R;rCFJ!8aFRs^ zv8WC$5z^GI#=~2YZBa*bCBg2hg4@QaRwO)x>Lza|dU?ymE?Q|;>mj3QGV7K8>fkk8 z@Sv@f=rTh?g=di-CoADnal;gFPW!}x!v=Az-uK_Voqxya_`0c>Ja9?n<-;%2hvGC; Lbd>Y|vJCz&)719t literal 0 HcmV?d00001 diff --git a/docs/media/tui-lock-v2/runtime/120x40/computer-disconnected.png b/docs/media/tui-lock-v2/runtime/120x40/computer-disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..1d3e647f56a0777b6338d03c79ff0ca10c14b115 GIT binary patch literal 9233 zcmeHMXH-*7w?2SW1uLP0pi%{eC=j}!A|RktDS{L!(g{)wRZtX^7OF%*5Jjp8p_l{| zUO;*$2nd9PL`rCp66!s?>)v($+`HEO@qPE#JL{~hoO5ROoO$-1Ju}bq@{W-%CkHz)xU`k?R1fK*|2;2dHbEA6KubBm-sJ}3k6F0vC)3EcL=KXgV*W4we zkuliU4{%Pa*L}tEE&uW$G*_sQx4d^o`1Q@kzHg!SmG?-%o;DNj?r#=y zAQNc$Ur-`7)P?Rd06?Vf>e_bTK^y*(535B%)^S}+(0JZ6(NT=eF*<~)X(zT>f_}}( zxk>LCUON4Ab#VL$Rwi2r0JLHu(Y37|Bcdpq<5V0U;RpcK#q<64(3A+Q%G=@CjKg6Y z3`dS%!kK#~pELr1>AQbcNX|c1V&JM++)ZT$AaY^+7*3IOyA^46nz5b)D@Rgfc`R?) zy!n~W%XrwJ-!Q+so3{do4i3(g5xDhunuhu?1Ohu`z5;8{45HqY^ZLdn0RZ0)jvix4 zVAUMIC)j0cBkSNE4f*?XMkQo#jjK*>t!b8a9rEj1B(7n4+5n-%BUyj&+Vf$5vqSu5% zwYB5F;367otdE^TfmY_occ;h$z9}fw;E^vK7Yi#QX`n@<{1r&pc3X7nQ2^N60h{x- z0+e$|VqtejcWSOpqUobSaXz`5#X90hwb-=f+?c^u9+mxN<&X#B^Vr`e1Y2basxWrw zQB|U2U?jRa8GV0osv0s&IR{Aj$&M56W=kz+J;AVT$I1TZCczq|iLu#mC z@9Opic!3^H3brm_)Z(>AnyI(*Sa}UR8N~qn?(;d=-gMcjmFbFS7jHfuB3xb}g{IBD zVMltx$)O2@t*NS6j@$0emOY5h*F5&#_i)x!StXQWxO>(9+w*)&@BboUGa*1E*Dt0< zbnsej#NN)ntKZy-3rie4@qeYu!~NhK^L{Z^Ka|Hm8t0x88%CXi`f?qrvPAZXo$k=^S=U4cO4%KN%&0zjcU=18 z#Qsi@Fn3$;YLDJg;G3wprr4>i&%y5y@w^u0S3bjjd+1ZW+_}o)7j;Uzg>aAR)4H-x zM?fz~fYE=SkJV>=(%k0U`0X%eRQ&PA;m$^pH0{zKmSdp@x~m*la3zJq0$q#TUabx-n+cb$H+v9NfmKC&g`AN2n_sFS`q zn}H!r_&ct{!4Gq&z%`~E=86$~sx=sja?NZKJPvAL26*BW1OEPd%l7&vr8gpCKQusA z|Epa**jbJ>1oGlj`1anW!?T@6Ck6J3EUQXCGL3s6PI%3#6KfX#1Txr}oJFF5`F)7C z^qb?42b(*j`7Tom*xEtF8losNV!)eItdxJ1goWHrh;{HdoIt)8UNTUoay5;cs-E5* zJfCk9&qTS9{$^`?aJey~<58uBNBl;OWQcn(hadR}5nYJv<&ikG+|367zpXk_$95hc zY%L3)Db-Uh$qhf)+n3NRK~)o8Hmq-7_-N4rX93|#S7?GA14iN*TAdu5-R8-uc)AKP zdDPHc=S|Dq1~FL<<3IN&QeJ1sDW31Ugg9)novK_Is4!Q(X!-(H&s}Q%{pPSo|B7DI zm1$IAZQa?BN{ehq|HFD2Tj^t5Z5fWjkzMU&IGk);?(cDvTr}u5{#$$mRIED2rF;aM z`|fLrBSNIpvyapT0TY#=^mHS}oXe2mq9edc;&k+|Vv_awz*k*bbJ+AvB; zX0q8nt7~Kp4PFZWaF^TmOzsN@lHwpWji%vA>R zLdLVKpY0etnKZ0*O=2(QsAmc8bQp2)dCQ+BQj=JpAsva7_(UFr`fqG>?@6D9KM6S- z5?mZuhclPJS&pR;P@Ce|*>&x%yO2@Z_-&%G%^TY#vV3L|O^%{sJYw@?s9x@-wa8

fM`v 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 literal 0 HcmV?d00001 diff --git a/src/cortex-tui/src/lock_v2_computer.rs b/src/cortex-tui/src/lock_v2_computer.rs index bc40bd6d..2c3fd572 100644 --- a/src/cortex-tui/src/lock_v2_computer.rs +++ b/src/cortex-tui/src/lock_v2_computer.rs @@ -4,10 +4,10 @@ //! file past the source-policy line-count baseline. use cortex_core::widgets::Message; -use cortex_engine::client::{ComputerKind, DISCONNECTED_RUNTIME}; +use cortex_engine::client::DISCONNECTED_RUNTIME; use crate::app::AppState; -use crate::ui::consts::{DISCONNECTED_TITLE, PLACEHOLDER_DISCONNECTED}; +use crate::ui::consts::DISCONNECTED_TITLE; /// Computer lock boards. Each filename is one live state. pub const COMPUTER_SCENE_IDS: &[&str] = &["computer-disconnected", "computer-cloud-default"]; @@ -19,17 +19,15 @@ pub const DISCONNECTED_RUNTIME_NARROW: &str = /// Apply a Computer lock scene. Returns `false` when `id` is not one of these. pub fn apply_computer_scene(id: &str, state: &mut AppState, width: u16) -> bool { + if !COMPUTER_SCENE_IDS.contains(&id) { + return false; + } match id { - "computer-disconnected" => { - seed_disconnected(state, width); - true - } - "computer-cloud-default" => { - seed_cloud_default(state); - true - } - _ => false, + "computer-disconnected" => seed_disconnected(state, width), + "computer-cloud-default" => seed_cloud_default(state), + _ => return false, } + true } fn seed_disconnected(state: &mut AppState, width: u16) { @@ -61,7 +59,8 @@ fn seed_cloud_default(state: &mut AppState) { /// Squeezed transcript text so wrapped rows still match engine copy. /// Scrollbar and box-drawing cells become spaces before collapse. -pub fn squeezed_plain(plain: &str) -> String { +#[cfg(test)] +fn squeezed_plain(plain: &str) -> String { plain .chars() .map(|c| { @@ -82,7 +81,9 @@ mod tests { use super::*; use crate::lock_v2::render_lock_v2_scene; use crate::lock_v2_ids::{LOCK_V2_NARROW_IDS, LOCK_V2_WIDE_IDS}; + use crate::ui::consts::PLACEHOLDER_DISCONNECTED; use cortex_core::style::{ERROR, TEXT_DIM}; + use cortex_engine::client::ComputerKind; const SIZES: [(u16, u16); 2] = [(120, 40), (40, 12)]; From de897c2ea8b5870d132a8234794f5281bcc824e1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 16:20:12 +0000 Subject: [PATCH 3/8] docs(tui): lock computer runtime inventory at 84/38 Re-render computer lock_v2 runtime frames, drop the stray narrow jobs.png so 40x12 is exactly 38 unique hashes, and wire scene_state with explicit computer arms. Co-authored-by: Mathis --- design/cli-lock-board-index.md | 13 ++++++++++--- docs/media/tui-lock-v2/index.md | 6 ++++-- docs/media/tui-lock-v2/runtime/40x12/jobs.png | Bin 3985 -> 0 bytes 3 files changed, 14 insertions(+), 5 deletions(-) delete mode 100644 docs/media/tui-lock-v2/runtime/40x12/jobs.png diff --git a/design/cli-lock-board-index.md b/design/cli-lock-board-index.md index d5543e23..2578d5ad 100644 --- a/design/cli-lock-board-index.md +++ b/design/cli-lock-board-index.md @@ -5,7 +5,7 @@ Generated from the id lists in the tree at `3035361` (`v0.1.10`): `is_lock_board()` in `src/cortex-tui/src/lock_boards.rs:44-99`, `LOCK_V2_WIDE_IDS` / `LOCK_V2_NARROW_IDS` in `src/cortex-tui/src/lock_v2.rs:33-147`. -Current Designer lock: **v2** (77 wide / 31 narrow), green focus `#1F4945`. +Current Designer lock: **v2** (84 wide / 38 narrow), green focus `#1F4945`. Committed PNGs under `docs/media/tui-lock/` and `docs/media/tui-lock-v2/` still include historical violet `#A78BFA` pixels (see those READMEs). The ids and tests below are the source of truth: @@ -57,7 +57,7 @@ shows those chords; cover those boards with `lock.render`, | `lock_proof::tests::distinct_states_render_distinct_frames` | v1 | only the 4 documented aliases share a frame | | `lock_proof::tests::no_smashed_tokens_anywhere` | v1 | wrapped copy never breaks tokens | | `lock_proof::tests::no_rounded_frame_glyphs_anywhere` | v1 | currently a no-op (no glyph assertions); TUI bleeds to terminal edges — see `docs/media/tui-lock/README.md` | -| `lock_v2::tests::lock_v2_wide_count_is_spec` | v2 | 77 / 31 ids | +| `lock_v2::tests::lock_v2_wide_count_is_spec` | v2 | 84 / 38 ids | | `lock_v2::tests::lock_v2_wide_frames_are_unique`, `lock_v2_narrow_frames_are_unique` | v2 | every id is a distinct frame | | `lock_v2::tests::slash_hover_is_not_banner_green_wash` | v2 | hover `#1A1A1A`; `#221A38` banned | | `style::tests::gray_chrome_palette_is_locked` (`cortex-core`) | palette | `ACCENT == #1F4945`, grays neutral, mint/cyan banned, gold retired | @@ -181,6 +181,11 @@ Every v2 id is covered by `lock_v2_wide_frames_are_unique` (and | `tokens-topright-warn` | — | real | uniqueness | fixture usage ≥ 90 % | | `compact-chat` | yes | real | uniqueness | `tui.type "/compact"` | | `slash-palette` | yes | real | `slash_hover_is_not_banner_green_wash` | flow *Slash* | +| `goal-chip-active` | yes | real | uniqueness | `lock.render` (`/goal` chip) | +| `goal-chip-paused` | yes | real | uniqueness | `lock.render` | +| `goal-chip-done` | yes | real | uniqueness | `lock.render` | +| `goal-chip-budget` | yes | real | uniqueness | `lock.render` | +| `goal-chip-blocked` | yes | real | uniqueness | `lock.render` | | `slash-model-typed` | yes | real | uniqueness | flow *Slash* (`/mod`) | | `model-list` | yes | real (`build_model_selector`) | uniqueness | flow *Model* + `api.models` | | `model-list-hover` | — | real | uniqueness | mouse fixture | @@ -208,6 +213,8 @@ Every v2 id is covered by `lock_v2_wide_frames_are_unique` (and | `sandbox` | — | **synthetic** | uniqueness | `tui.type "/sandbox"` (`build_sandbox_selector`) | | `sandbox-deny` | — | **synthetic** | uniqueness | flow *Permission* (deny) via `CortexError::SandboxDenied` | | `cloud-handoff` | — | seed | uniqueness | `tui.type "& …"` | +| `computer-disconnected` | yes | real | `lock_v2_computer::tests::*` | This PC/SSH fail-closed — no substitute runtime | +| `computer-cloud-default` | yes | real | `lock_v2_computer::tests::*` | Computer · Cloud welcome default | | `diagnostics` | yes | seed | uniqueness | flow *Errors* (diagnostics tool) | | `interrupt-stopped` | yes | seed | runtime path `interrupt_records_stopped_once` | flow *Cancel* | | `error-unavailable` | — | real | uniqueness | flow *Errors* (503) | @@ -241,7 +248,7 @@ Every v2 id is covered by `lock_v2_wide_frames_are_unique` (and | Pack | Ids | Sizes | Frames | Live / real | Painted or synthetic | PNGs in repo (all violet) | |---|---|---|---|---|---|---| | v1 | 72 | 40×12, 120×40 | 144 (+144 macOS composites) | 17 | 51 painted + 4 aliases | 65/72 files carry `#A78BFA` at each size | -| v2 | 77 wide / 31 narrow | 120×40 / 40×12 | 108 | 63 (5 seeded) | 14 synthetic | runtime 73/77 + 30/31; designer boards 68/77 + 29/31 | +| v2 | 84 wide / 38 narrow | 120×40 / 40×12 | 122 | 70 (5 seeded) | 14 synthetic | runtime 84/84 + 38/38; designer boards 84/84 + 38/38 | Regenerate captures: `./scripts/render-tui-lock.sh`, `./scripts/render-tui-lock-v2.sh`, `python3 docs/media/tui-lock-v2/tools/render_lock_v2.py --index`. diff --git a/docs/media/tui-lock-v2/index.md b/docs/media/tui-lock-v2/index.md index 8b8de734..26edd6b5 100644 --- a/docs/media/tui-lock-v2/index.md +++ b/docs/media/tui-lock-v2/index.md @@ -90,8 +90,8 @@ Regenerate: `python3 tools/render_lock_v2.py --index` (fetches IBM Plex Mono on | `error-unavailable` | API down — product-facing error | [120x40](120x40/error-unavailable.png) | — | | `offline` | Network unreachable — held composer, distinct from API-down | [120x40](120x40/offline.png) | [40x12](40x12/offline.png) | | `rate-limit` | HTTP 429 rate limited — retry-after, distinct from quota | [120x40](120x40/rate-limit.png) | [40x12](40x12/rate-limit.png) | -| `computer-disconnected` | This PC/SSH fail-closed — no connected Code session | [120x40](120x40/computer-disconnected.png) | [40x12](40x12/computer-disconnected.png) | -| `computer-cloud-default` | Computer · Cloud shipped default (welcome info card) | [120x40](120x40/computer-cloud-default.png) | [40x12](40x12/computer-cloud-default.png) | +| `computer-disconnected` | This PC/SSH fail-closed — no connected Code session | [120x40](120x40/computer-disconnected.png) · [runtime](runtime/120x40/computer-disconnected.png) | [40x12](40x12/computer-disconnected.png) · [runtime](runtime/40x12/computer-disconnected.png) | +| `computer-cloud-default` | Computer · Cloud shipped default (welcome info card) | [120x40](120x40/computer-cloud-default.png) · [runtime](runtime/120x40/computer-cloud-default.png) | [40x12](40x12/computer-cloud-default.png) · [runtime](runtime/40x12/computer-cloud-default.png) | | `tool-tiles` | Grouped tool calls expanded — Read / Grep / Shell | [120x40](120x40/tool-tiles.png) | — | | `tool-tiles-collapsed` | Grouped tool calls collapsed | [120x40](120x40/tool-tiles-collapsed.png) | — | | `shell-running` | Live Shell tile with output | [120x40](120x40/shell-running.png) | — | @@ -121,3 +121,5 @@ Regenerate: `python3 tools/render_lock_v2.py --index` (fetches IBM Plex Mono on | `undo-sheet` | /undo /redo /rewind sheet | [120x40](120x40/undo-sheet.png) | [40x12](40x12/undo-sheet.png) | **89** boards at 120x40 · **43** at 40x12 · 132 PNGs. + +Runtime MockTerminal pack (same ids): [`runtime/120x40`](runtime/120x40/) (89) · [`runtime/40x12`](runtime/40x12/) (43). diff --git a/docs/media/tui-lock-v2/runtime/40x12/jobs.png b/docs/media/tui-lock-v2/runtime/40x12/jobs.png deleted file mode 100644 index 39f8c5db642dc223304a8031f31632974e34f6e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3985 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 Date: Sun, 13 Sep 2026 16:30:08 +0000 Subject: [PATCH 4/8] fix(tui): satisfy clippy expect_fun_call on lock_v2 tests Co-authored-by: Mathis --- src/cortex-tui/src/lock_v2_computer.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/cortex-tui/src/lock_v2_computer.rs b/src/cortex-tui/src/lock_v2_computer.rs index 2c3fd572..4c4ca8f0 100644 --- a/src/cortex-tui/src/lock_v2_computer.rs +++ b/src/cortex-tui/src/lock_v2_computer.rs @@ -128,8 +128,8 @@ mod tests { #[test] fn disconnected_uses_engine_copy_and_fail_closed() { for (width, height) in SIZES { - let frame = - render_lock_v2_scene("computer-disconnected", width, height).expect(id_at(width)); + let frame = render_lock_v2_scene("computer-disconnected", width, height) + .expect("computer-disconnected"); let plain = &frame.plain; let squeezed = squeezed_plain(plain); assert!( @@ -194,14 +194,6 @@ mod tests { } } - fn id_at(width: u16) -> &'static str { - if width <= 40 { - "computer-disconnected-narrow" - } else { - "computer-disconnected-wide" - } - } - #[test] fn disconnected_title_is_error_red_and_body_is_dim() { for (width, height) in SIZES { From e02b3147e52f9715b3f9b2f2093732ecec81b645 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 16:39:23 +0000 Subject: [PATCH 5/8] docs(tui): correct lock_v2 inventory and computer scene kinds Co-authored-by: Mathis --- design/cli-lock-board-index.md | 16 +++++++++------- docs/media/tui-lock-v2/SPEC.md | 5 ++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/design/cli-lock-board-index.md b/design/cli-lock-board-index.md index 2578d5ad..67e95969 100644 --- a/design/cli-lock-board-index.md +++ b/design/cli-lock-board-index.md @@ -3,9 +3,9 @@ Generated from the id lists in the tree at `3035361` (`v0.1.10`): `lock_scene_ids()` in `src/cortex-tui/src/lock_proof.rs:53-131`, `is_lock_board()` in `src/cortex-tui/src/lock_boards.rs:44-99`, -`LOCK_V2_WIDE_IDS` / `LOCK_V2_NARROW_IDS` in `src/cortex-tui/src/lock_v2.rs:33-147`. +`LOCK_V2_WIDE_IDS` / `LOCK_V2_NARROW_IDS` in `src/cortex-tui/src/lock_v2_ids.rs`. -Current Designer lock: **v2** (84 wide / 38 narrow), green focus `#1F4945`. +Current Designer lock: **v2** (86 wide / 40 narrow), green focus `#1F4945`. Committed PNGs under `docs/media/tui-lock/` and `docs/media/tui-lock-v2/` still include historical violet `#A78BFA` pixels (see those READMEs). The ids and tests below are the source of truth: @@ -57,7 +57,7 @@ shows those chords; cover those boards with `lock.render`, | `lock_proof::tests::distinct_states_render_distinct_frames` | v1 | only the 4 documented aliases share a frame | | `lock_proof::tests::no_smashed_tokens_anywhere` | v1 | wrapped copy never breaks tokens | | `lock_proof::tests::no_rounded_frame_glyphs_anywhere` | v1 | currently a no-op (no glyph assertions); TUI bleeds to terminal edges — see `docs/media/tui-lock/README.md` | -| `lock_v2::tests::lock_v2_wide_count_is_spec` | v2 | 84 / 38 ids | +| `lock_v2::tests::lock_v2_wide_count_is_spec` | v2 | 86 / 40 ids | | `lock_v2::tests::lock_v2_wide_frames_are_unique`, `lock_v2_narrow_frames_are_unique` | v2 | every id is a distinct frame | | `lock_v2::tests::slash_hover_is_not_banner_green_wash` | v2 | hover `#1A1A1A`; `#221A38` banned | | `style::tests::gray_chrome_palette_is_locked` (`cortex-core`) | palette | `ACCENT == #1F4945`, grays neutral, mint/cyan banned, gold retired | @@ -145,7 +145,7 @@ Kinds: **painted** = `lock_boards.rs` painter (not the runtime view); | `sandbox_deny` | painted | `red_and_amber_stay_on_diagnostics` | flow *Permission* (deny) | | `mcp_drop` | painted | `red_and_amber_stay_on_diagnostics`; runtime path: `runner::event_loop::tests::mcp_disconnect_without_user_stop_is_a_drop` | flow *MCP* (kill peer) | -## Lock v2 — 77 ids (`docs/media/tui-lock-v2/`), 31 also at 40×12 +## Lock v2 — 86 ids (`docs/media/tui-lock-v2/`), 40 also at 40×12 Kinds: **real** = production view/builder with a real `AppState`; **seed** = real view but the runtime-emitted line is inserted as @@ -213,8 +213,10 @@ Every v2 id is covered by `lock_v2_wide_frames_are_unique` (and | `sandbox` | — | **synthetic** | uniqueness | `tui.type "/sandbox"` (`build_sandbox_selector`) | | `sandbox-deny` | — | **synthetic** | uniqueness | flow *Permission* (deny) via `CortexError::SandboxDenied` | | `cloud-handoff` | — | seed | uniqueness | `tui.type "& …"` | -| `computer-disconnected` | yes | real | `lock_v2_computer::tests::*` | This PC/SSH fail-closed — no substitute runtime | -| `computer-cloud-default` | yes | real | `lock_v2_computer::tests::*` | Computer · Cloud welcome default | +| `offline` | yes | seed | `lock_v2_network::tests::*` | Network unreachable — no API-down copy | +| `rate-limit` | yes | seed | `lock_v2_network::tests::*` | HTTP 429 — distinct from quota | +| `computer-disconnected` | yes | seed | `lock_v2_computer::tests::*` | This PC/SSH fail-closed — no substitute runtime | +| `computer-cloud-default` | yes | seed | `lock_v2_computer::tests::*` | Computer · Cloud welcome default | | `diagnostics` | yes | seed | uniqueness | flow *Errors* (diagnostics tool) | | `interrupt-stopped` | yes | seed | runtime path `interrupt_records_stopped_once` | flow *Cancel* | | `error-unavailable` | — | real | uniqueness | flow *Errors* (503) | @@ -248,7 +250,7 @@ Every v2 id is covered by `lock_v2_wide_frames_are_unique` (and | Pack | Ids | Sizes | Frames | Live / real | Painted or synthetic | PNGs in repo (all violet) | |---|---|---|---|---|---|---| | v1 | 72 | 40×12, 120×40 | 144 (+144 macOS composites) | 17 | 51 painted + 4 aliases | 65/72 files carry `#A78BFA` at each size | -| v2 | 84 wide / 38 narrow | 120×40 / 40×12 | 122 | 70 (5 seeded) | 14 synthetic | runtime 84/84 + 38/38; designer boards 84/84 + 38/38 | +| v2 | 86 wide / 40 narrow | 120×40 / 40×12 | 126 | 72 (7 seeded) | 14 synthetic | runtime 86/86 + 40/40; designer boards 86/86 + 40/40 | Regenerate captures: `./scripts/render-tui-lock.sh`, `./scripts/render-tui-lock-v2.sh`, `python3 docs/media/tui-lock-v2/tools/render_lock_v2.py --index`. diff --git a/docs/media/tui-lock-v2/SPEC.md b/docs/media/tui-lock-v2/SPEC.md index f7c756a1..4ed84fea 100644 --- a/docs/media/tui-lock-v2/SPEC.md +++ b/docs/media/tui-lock-v2/SPEC.md @@ -5,7 +5,10 @@ Design-only deliverable for the full TUI redesign. Layout language is taken composer with the model chip in the bottom border, Settings modal with search + categorised rows + tip/nav footer, slash autocomplete above the composer, effort radios under `/model`, token counter top-right, footer shortcut strip) and -re-skinned to the Cortex chrome. **No runtime code changes ship with this pack.** +re-skinned to the Cortex chrome. Designer boards stay design-only; runtime +captures use `MinimalSessionView` plus lock flags (`computer_held`, +`show_computer_default`, `offline_held`, `rate_limit_held`) so MockTerminal +matches live chrome. - Boards: [`index.md`](index.md) — 89 boards at 120×40, 43 of them also at 40×12 (132 PNGs). - Grids: `txt//.txt` — the exact character grid of every board (diff a From 389eb41ac252393eb94e5796326aac5c9511c115 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 17:10:34 +0000 Subject: [PATCH 6/8] style(tui): rustfmt footer hold after rebase Co-authored-by: Mathis --- src/cortex-tui/src/views/minimal_session/view.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cortex-tui/src/views/minimal_session/view.rs b/src/cortex-tui/src/views/minimal_session/view.rs index 6fd61b5a..1fa5695f 100644 --- a/src/cortex-tui/src/views/minimal_session/view.rs +++ b/src/cortex-tui/src/views/minimal_session/view.rs @@ -741,9 +741,7 @@ pub const PALETTE_FOOTER_HINT_SHORT: &str = "Enter:send | Ctrl+x:shortcuts"; impl<'a> MinimalSessionView<'a> { fn footer_set(&self, is_task_running: bool, width: u16) -> FooterSet { - if self.app_state.offline_held - || self.app_state.quota_held - || self.app_state.computer_held + if self.app_state.offline_held || self.app_state.quota_held || self.app_state.computer_held { return FooterSet::Unavailable; } From 2c9a25b3df0de47bae82e2b7957deabcaafc16b7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 17:15:01 +0000 Subject: [PATCH 7/8] fix(ci): isolate CodeBuild PR concurrency by event pull_request and pull_request_target shared one group on github.ref, so one cancelled the other and the cancelled job rendered as matrix.context. Co-authored-by: Mathis --- .github/workflows/codebuild.yml | 5 ++++- scripts/readiness/test_codebuild.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codebuild.yml b/.github/workflows/codebuild.yml index 4da317df..81ae82b3 100644 --- a/.github/workflows/codebuild.yml +++ b/.github/workflows/codebuild.yml @@ -23,7 +23,10 @@ name: CodeBuild CI workflow_dispatch: concurrency: - group: codebuild-${{ github.workflow }}-${{ github.ref }} + # pull_request and pull_request_target share github.ref (the PR). If they + # share a group, one cancels the other and the cancelled job shows up as + # `matrix.context` because the name expression never expands. + group: codebuild-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: true permissions: diff --git a/scripts/readiness/test_codebuild.py b/scripts/readiness/test_codebuild.py index 80798857..b8ed2635 100644 --- a/scripts/readiness/test_codebuild.py +++ b/scripts/readiness/test_codebuild.py @@ -100,6 +100,9 @@ def test_pull_requests_use_trusted_buildspec_and_uncached_projects(self): self.assertIn("github.event_name != 'pull_request'", text) self.assertIn("github.event_name == 'pull_request_target'", text) self.assertIn("github.event_name == 'push'", text) + group = self.workflow["concurrency"]["group"] + self.assertIn("github.event_name", group) + self.assertIn("github.ref", group) self.assertIn("logs-only", text) def test_start_build_is_skipped_without_role_variable(self): From b661b0e2681c43903172a060875fcc8470692824 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 17:35:59 +0000 Subject: [PATCH 8/8] docs(tui): sync lock_v2 inventory after #70 rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep COR-23 computer scenes and #70 consent/@file/undo boards. Combined SPEC §7 set is 89 wide / 43 narrow (132 PNGs). Co-authored-by: Mathis --- design/cli-lock-board-index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/design/cli-lock-board-index.md b/design/cli-lock-board-index.md index 67e95969..18877f75 100644 --- a/design/cli-lock-board-index.md +++ b/design/cli-lock-board-index.md @@ -5,7 +5,7 @@ Generated from the id lists in the tree at `3035361` (`v0.1.10`): `is_lock_board()` in `src/cortex-tui/src/lock_boards.rs:44-99`, `LOCK_V2_WIDE_IDS` / `LOCK_V2_NARROW_IDS` in `src/cortex-tui/src/lock_v2_ids.rs`. -Current Designer lock: **v2** (86 wide / 40 narrow), green focus `#1F4945`. +Current Designer lock: **v2** (89 wide / 43 narrow), green focus `#1F4945`. Committed PNGs under `docs/media/tui-lock/` and `docs/media/tui-lock-v2/` still include historical violet `#A78BFA` pixels (see those READMEs). The ids and tests below are the source of truth: @@ -57,7 +57,7 @@ shows those chords; cover those boards with `lock.render`, | `lock_proof::tests::distinct_states_render_distinct_frames` | v1 | only the 4 documented aliases share a frame | | `lock_proof::tests::no_smashed_tokens_anywhere` | v1 | wrapped copy never breaks tokens | | `lock_proof::tests::no_rounded_frame_glyphs_anywhere` | v1 | currently a no-op (no glyph assertions); TUI bleeds to terminal edges — see `docs/media/tui-lock/README.md` | -| `lock_v2::tests::lock_v2_wide_count_is_spec` | v2 | 86 / 40 ids | +| `lock_v2::tests::lock_v2_wide_count_is_spec` | v2 | 89 / 43 ids | | `lock_v2::tests::lock_v2_wide_frames_are_unique`, `lock_v2_narrow_frames_are_unique` | v2 | every id is a distinct frame | | `lock_v2::tests::slash_hover_is_not_banner_green_wash` | v2 | hover `#1A1A1A`; `#221A38` banned | | `style::tests::gray_chrome_palette_is_locked` (`cortex-core`) | palette | `ACCENT == #1F4945`, grays neutral, mint/cyan banned, gold retired | @@ -250,7 +250,7 @@ Every v2 id is covered by `lock_v2_wide_frames_are_unique` (and | Pack | Ids | Sizes | Frames | Live / real | Painted or synthetic | PNGs in repo (all violet) | |---|---|---|---|---|---|---| | v1 | 72 | 40×12, 120×40 | 144 (+144 macOS composites) | 17 | 51 painted + 4 aliases | 65/72 files carry `#A78BFA` at each size | -| v2 | 86 wide / 40 narrow | 120×40 / 40×12 | 126 | 72 (7 seeded) | 14 synthetic | runtime 86/86 + 40/40; designer boards 86/86 + 40/40 | +| v2 | 89 wide / 43 narrow | 120×40 / 40×12 | 132 | 72 (7 seeded) | 14 synthetic | runtime 89/89 + 43/43; designer boards 89/89 + 43/43 | Regenerate captures: `./scripts/render-tui-lock.sh`, `./scripts/render-tui-lock-v2.sh`, `python3 docs/media/tui-lock-v2/tools/render_lock_v2.py --index`.