Stop the shell asserting things that are not true

The footer was rebuilt on a real ledger two commits ago. The rest of the shell
was never audited the same way, and a multi-agent pass over it found the same
class of defect everywhere else:

- A sidebar card reading "Buzz · lumbridgecode / connected · signed identity"
  in the success colour. lumbridge-buzz is not a dependency of this binary.
- A saved host "amd-server", and a WORKTREES section with five entries and a
  working selector, backed by a lumbridge-git crate that does not exist.
- A first-run workspace of six panes announcing "Codex · runtime / metal ·
  Tailscale SSH", "Claude Code · UI / MacBook Air · local" and a Pi pane on a
  saved host. Every one of them was a /bin/sh, and the machine names were this
  developer's.
- A declared "Pi · spark-1 · laguna-s-2.1" usage profile with no probe of any
  kind behind it. Declaring a profile promises the gap is real; that one could
  never be filled.
- FOOTER_CENTER = "Codex · ChatGPT subscription · 62% window remaining",
  rendered by the Floem shell. Decision 0013 names that exact form as the thing
  that must never be shown.
- The header's PTY count painted green unconditionally, so "0/5 LIVE PTYS" read
  as success. runtime_rows already had the right rule three hundred lines away.
- A browser panel describing itself as "An isolated system-web-engine surface"
  on the chooser screen where you pick it. There is no web engine in this build.

First run is now three real local shells, and a pane claims a harness when one
has actually been launched into it. The seed mapping stays for when that is
possible.

Also removes the only unsafe block in the shell: a test set LUMBRIDGE_*_PROBE
through the environment, which needs unsafe under edition 2024 and silently
disabled both probes for every other test in the binary. Replaced with
UsageFeedOptions passed to start_with.

Clippy pedantic on the spike goes 79 -> 15 against root CI's -D warnings, so
graduating it into the workspace is not gated on a warning cleanup. The four
remaining too_many_lines are the render split, which the sidebar work needs to
do anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Metal Agent
2026-08-31 22:54:24 -07:00
co-authored by Claude Opus 5
parent 219c674aea
commit 834b73e831
5 changed files with 223 additions and 201 deletions
+12 -20
View File
@@ -12,8 +12,7 @@ use floem::{
window::WindowConfig, window::WindowConfig,
}; };
use lumbridge_spike_model::{ use lumbridge_spike_model::{
FOOTER_CENTER, FOOTER_RIGHT, FocusDirection, PaneId, PaneState, ShellAction, ShellModel, FOOTER_FIXTURE_NOTICE, FocusDirection, PaneId, PaneState, ShellAction, ShellModel, SurfaceKind,
SurfaceKind, WORKSPACES,
}; };
const BG: Color = Color::from_rgb8(9, 12, 18); const BG: Color = Color::from_rgb8(9, 12, 18);
@@ -132,22 +131,16 @@ fn render_pane_card(pane: PaneState, selected: bool, model: RwSignal<ShellModel>
} }
fn sidebar() -> impl IntoView { fn sidebar() -> impl IntoView {
let worktrees = Stack::from_iter(WORKSPACES.into_iter().enumerate().map(|(index, name)| { // The worktree list this shell used to render came from a five-entry
Stack::vertical(( // fixture naming this developer's machines, backed by no git integration.
Label::new(name), // The GPUI shell's copy was deleted in the same pass; this one renders the
Label::new(if index == 0 { "main · metal" } else { "" }) // workspace it is actually open on.
.style(|s| s.font_size(10.0).color(MUTED).margin_top(2.0)), let worktrees = Stack::vertical((Label::new("Lumbridge Code"),)).style(|s| {
)) s.flex_col()
.style(move |s| { .padding_horiz(8.0)
s.width_full() .padding_vert(7.0)
.padding_vert(7.0) .color(TEXT)
.padding_horiz(10.0) });
.margin_bottom(3.0)
.color(if index == 0 { TEXT } else { MUTED })
.apply_if(index == 0, |s| s.background(PANEL_ALT).border_radius(5.0))
})
}))
.style(|s| s.flex_col().padding_horiz(8.0));
Stack::vertical(( Stack::vertical((
Label::new("ATTENTION · 1").style(|s| s.font_size(10.0).color(ATTENTION).padding(12.0)), Label::new("ATTENTION · 1").style(|s| s.font_size(10.0).color(ATTENTION).padding(12.0)),
@@ -434,8 +427,7 @@ fn app_view() -> impl IntoView {
}) })
}) })
.style(|s| s.flex_grow(1.0)), .style(|s| s.flex_grow(1.0)),
Label::new(FOOTER_CENTER).style(|s| s.flex_grow(1.0)), Label::new(FOOTER_FIXTURE_NOTICE).style(|s| s.flex_grow(1.0)),
Label::new(FOOTER_RIGHT),
)) ))
.style(|s| { .style(|s| {
s.height(32.0) s.height(32.0)
+112 -91
View File
@@ -16,7 +16,7 @@ use lumbridge_runtime::{
}; };
use lumbridge_spike_model::{ use lumbridge_spike_model::{
ActionOutcome, OutputSource, PaneId as FixturePaneId, PaneStatus, ShellAction, ShellModel, ActionOutcome, OutputSource, PaneId as FixturePaneId, PaneStatus, ShellAction, ShellModel,
SurfaceKind, WORKSPACES, SurfaceKind,
}; };
use lumbridge_storage::Store; use lumbridge_storage::Store;
use lumbridge_terminal::{ use lumbridge_terminal::{
@@ -28,16 +28,63 @@ use lumbridge_terminal::{
use panel_registry::{PanelId, PanelKind, PanelRegistry, SeedPane}; use panel_registry::{PanelId, PanelKind, PanelRegistry, SeedPane};
use usage_feed::{UsageFeed, UsageSegment}; use usage_feed::{UsageFeed, UsageSegment};
// A colour is read as six hex digits, so `0x8b_c8ff` would be harder to check
// against a design token than `0x8bc8ff`, not easier. Scoped to the palette and
// the two terminal colour tables; every other literal keeps the lint.
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const BG: u32 = 0x090c12; const BG: u32 = 0x090c12;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const PANEL: u32 = 0x101620; const PANEL: u32 = 0x101620;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const PANEL_ALT: u32 = 0x151d29; const PANEL_ALT: u32 = 0x151d29;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const PANEL_ACTIVE: u32 = 0x182334; const PANEL_ACTIVE: u32 = 0x182334;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const BORDER: u32 = 0x263246; const BORDER: u32 = 0x263246;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const BORDER_QUIET: u32 = 0x1c2636; const BORDER_QUIET: u32 = 0x1c2636;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const TEXT: u32 = 0xdbe5f4; const TEXT: u32 = 0xdbe5f4;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const MUTED: u32 = 0x8290a8; const MUTED: u32 = 0x8290a8;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const ACCENT: u32 = 0x68b5f8; const ACCENT: u32 = 0x68b5f8;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const ATTENTION: u32 = 0xf1b96a; const ATTENTION: u32 = 0xf1b96a;
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
const SUCCESS: u32 = 0x70d6a8; const SUCCESS: u32 = 0x70d6a8;
const TIMING_SAMPLE_LIMIT: usize = 256; const TIMING_SAMPLE_LIMIT: usize = 256;
const RUNTIME_POLL_INTERVAL: Duration = Duration::from_millis(16); const RUNTIME_POLL_INTERVAL: Duration = Duration::from_millis(16);
@@ -94,7 +141,6 @@ struct LumbridgeShell {
/// Which decision-shelf choice each panel has selected. Selecting one is /// Which decision-shelf choice each panel has selected. Selecting one is
/// inert by design: it prepares nothing and runs nothing. /// inert by design: it prepares nothing and runs nothing.
shelf_choice: BTreeMap<PanelId, usize>, shelf_choice: BTreeMap<PanelId, usize>,
active_worktree: usize,
add_panel_chooser_open: bool, add_panel_chooser_open: bool,
root_focus: FocusHandle, root_focus: FocusHandle,
} }
@@ -339,6 +385,34 @@ fn terminal_paint_rows(snapshot: &TerminalSnapshot) -> Vec<Vec<TerminalPaintRun>
.collect() .collect()
} }
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
/// Narrows an already-clamped dimension.
///
/// Every caller clamps into `2.0..=u16::MAX` first, so this is a narrowing of a
/// value known to fit — but `as` would silently produce garbage if a caller ever
/// stopped clamping, and a NaN would become zero. This saturates instead, which
/// is why the `with_cell_size` expect below is honest rather than hopeful.
fn clamp_to_u16(value: f32) -> u16 {
if value.is_nan() {
return 1;
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "clamped into u16 range on the line above"
)]
{
value.clamp(0.0, f32::from(u16::MAX)) as u16
}
}
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
fn terminal_color(color: TerminalColor) -> u32 { fn terminal_color(color: TerminalColor) -> u32 {
match color { match color {
TerminalColor::Rgb { red, green, blue } => { TerminalColor::Rgb { red, green, blue } => {
@@ -377,6 +451,10 @@ fn terminal_color(color: TerminalColor) -> u32 {
} }
} }
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
fn indexed_terminal_color(index: u8) -> u32 { fn indexed_terminal_color(index: u8) -> u32 {
const ANSI: [u32; 16] = [ const ANSI: [u32; 16] = [
0x1d2430, 0xff6b6b, 0x70d6a8, 0xf1c76a, 0x68b5f8, 0xc79bf2, 0x63d5da, 0xdbe5f4, 0x6d7a91, 0x1d2430, 0xff6b6b, 0x70d6a8, 0xf1c76a, 0x68b5f8, 0xc79bf2, 0x63d5da, 0xdbe5f4, 0x6d7a91,
@@ -427,10 +505,10 @@ fn terminal_dimensions_for_window(
.floor() .floor()
.clamp(20.0, f32::from(u16::MAX)); .clamp(20.0, f32::from(u16::MAX));
TerminalDimensions::with_cell_size( TerminalDimensions::with_cell_size(
rows as u16, clamp_to_u16(rows),
columns as u16, clamp_to_u16(columns),
TERMINAL_CELL_WIDTH.round() as u16, clamp_to_u16(TERMINAL_CELL_WIDTH.round()),
TERMINAL_CELL_HEIGHT.round() as u16, clamp_to_u16(TERMINAL_CELL_HEIGHT.round()),
) )
.expect("geometry clamps terminal dimensions above zero") .expect("geometry clamps terminal dimensions above zero")
} }
@@ -513,7 +591,7 @@ fn load_panel_registry() -> (Option<Store>, PanelRegistry, String) {
let Some(path) = workspace_database_path() else { let Some(path) = workspace_database_path() else {
return ( return (
None, None,
PanelRegistry::seeded(), PanelRegistry::first_run(),
"layout memory-only · no data directory".to_owned(), "layout memory-only · no data directory".to_owned(),
); );
}; };
@@ -522,7 +600,7 @@ fn load_panel_registry() -> (Option<Store>, PanelRegistry, String) {
{ {
return ( return (
None, None,
PanelRegistry::seeded(), PanelRegistry::first_run(),
format!("layout memory-only · {error}"), format!("layout memory-only · {error}"),
); );
} }
@@ -531,7 +609,7 @@ fn load_panel_registry() -> (Option<Store>, PanelRegistry, String) {
Err(error) => { Err(error) => {
return ( return (
None, None,
PanelRegistry::seeded(), PanelRegistry::first_run(),
format!("layout memory-only · {error}"), format!("layout memory-only · {error}"),
); );
} }
@@ -541,18 +619,18 @@ fn load_panel_registry() -> (Option<Store>, PanelRegistry, String) {
Ok(panels) => (Some(store), panels, "layout restored · SQLite".to_owned()), Ok(panels) => (Some(store), panels, "layout restored · SQLite".to_owned()),
Err(error) => ( Err(error) => (
Some(store), Some(store),
PanelRegistry::seeded(), PanelRegistry::first_run(),
format!("invalid layout ignored · {error}"), format!("invalid layout ignored · {error}"),
), ),
}, },
Ok(None) => ( Ok(None) => (
Some(store), Some(store),
PanelRegistry::seeded(), PanelRegistry::first_run(),
"layout ready · SQLite".to_owned(), "layout ready · SQLite".to_owned(),
), ),
Err(error) => ( Err(error) => (
Some(store), Some(store),
PanelRegistry::seeded(), PanelRegistry::first_run(),
format!("layout read failed · {error}"), format!("layout read failed · {error}"),
), ),
} }
@@ -642,7 +720,6 @@ impl LumbridgeShell {
usage: UsageFeed::start(), usage: UsageFeed::start(),
surfaces: BTreeMap::new(), surfaces: BTreeMap::new(),
shelf_choice: BTreeMap::new(), shelf_choice: BTreeMap::new(),
active_worktree: 0,
add_panel_chooser_open: false, add_panel_chooser_open: false,
root_focus, root_focus,
} }
@@ -655,7 +732,7 @@ impl LumbridgeShell {
let result = self let result = self
.panels .panels
.to_json() .to_json()
.map_err(|error| error.to_owned()) .map_err(|error| error.clone())
.and_then(|json| { .and_then(|json| {
store store
.save_workspace_snapshot(WORKSPACE_SNAPSHOT_ID, &json) .save_workspace_snapshot(WORKSPACE_SNAPSHOT_ID, &json)
@@ -908,13 +985,6 @@ impl LumbridgeShell {
cx.notify(); cx.notify();
} }
fn select_worktree(&mut self, index: usize, window: &mut Window, cx: &mut Context<Self>) {
self.timing.mark_dispatch();
self.active_worktree = index.min(WORKSPACES.len().saturating_sub(1));
window.focus(&self.root_focus);
cx.notify();
}
fn select_pane_at(&mut self, index: usize, window: &mut Window, cx: &mut Context<Self>) { fn select_pane_at(&mut self, index: usize, window: &mut Window, cx: &mut Context<Self>) {
self.timing.mark_dispatch(); self.timing.mark_dispatch();
if self.panels.select_attached_at(index) { if self.panels.select_attached_at(index) {
@@ -1204,7 +1274,7 @@ impl LumbridgeShell {
|| run.style.contains(TerminalCellStyle::DOTTED_UNDERLINE) || run.style.contains(TerminalCellStyle::DOTTED_UNDERLINE)
|| run.style.contains(TerminalCellStyle::DASHED_UNDERLINE) || run.style.contains(TerminalCellStyle::DASHED_UNDERLINE)
|| run.hyperlink, || run.hyperlink,
|view| view.underline(), gpui::Styled::underline,
) )
.when(run.style.contains(TerminalCellStyle::STRIKEOUT), |view| { .when(run.style.contains(TerminalCellStyle::STRIKEOUT), |view| {
view.line_through() view.line_through()
@@ -1247,7 +1317,7 @@ impl LumbridgeShell {
"BROWSER", "BROWSER",
vec![ vec![
"about:blank".to_owned(), "about:blank".to_owned(),
"Content process: isolated".to_owned(), "No web engine is embedded in this build.".to_owned(),
"Choose a URL or open externally ↗".to_owned(), "Choose a URL or open externally ↗".to_owned(),
], ],
), ),
@@ -1256,7 +1326,7 @@ impl LumbridgeShell {
"MARKDOWN", "MARKDOWN",
vec![ vec![
"# Untitled note".to_owned(), "# Untitled note".to_owned(),
"".to_owned(), String::new(),
"Local-first workspace document.".to_owned(), "Local-first workspace document.".to_owned(),
], ],
), ),
@@ -1264,7 +1334,7 @@ impl LumbridgeShell {
SurfaceKind::Review, SurfaceKind::Review,
"REVIEW", "REVIEW",
vec![ vec![
"Working tree ready for review.".to_owned(), "No working tree is loaded.".to_owned(),
"No approval has been requested.".to_owned(), "No approval has been requested.".to_owned(),
"0 files selected".to_owned(), "0 files selected".to_owned(),
], ],
@@ -2069,18 +2139,10 @@ impl LumbridgeShell {
.count(); .count();
let total = self.live_terminals.len(); let total = self.live_terminals.len();
let local = RuntimeRow { let local = RuntimeRow {
label: "metal", label: "this machine",
detail: format!("{running}/{total} live PTYs"), detail: format!("{running}/{total} live PTYs"),
tone: if running == 0 { MUTED } else { SUCCESS }, tone: if running == 0 { MUTED } else { SUCCESS },
}; };
// Remote runtimes are a Phase 1 deliverable. Until the SSH stdio
// transport exists, the only honest status for a saved host is that
// nothing is connected to it.
let remote = RuntimeRow {
label: "amd-server",
detail: "saved host · no runtime connected".to_owned(),
tone: MUTED,
};
// The usage adapter is neither a host nor an endpoint, but its health // The usage adapter is neither a host nor an endpoint, but its health
// belongs beside them: it is the reason the footer can or cannot answer. // belongs beside them: it is the reason the footer can or cannot answer.
let adapter = RuntimeRow { let adapter = RuntimeRow {
@@ -2097,7 +2159,7 @@ impl LumbridgeShell {
SUCCESS SUCCESS
}, },
}; };
vec![local, remote, adapter] vec![local, adapter]
} }
/// The standing of every harness, always, regardless of what is selected. /// The standing of every harness, always, regardless of what is selected.
@@ -2373,41 +2435,6 @@ impl Render for LumbridgeShell {
shell.select_pane(id, window, cx); shell.select_pane(id, window, cx);
})) }))
})) }))
.child(
div()
.px_4()
.py_2()
.text_xs()
.text_color(rgb(MUTED))
.child("WORKTREES"),
)
.children(WORKSPACES.into_iter().enumerate().map(|(index, name)| {
let active = index == self.active_worktree;
div()
.id(("worktree", index as u64))
.cursor_pointer()
.mx_2()
.mb_1()
.px_3()
.py_2()
.rounded(px(5.0))
.when(active, |view| view.bg(rgb(PANEL_ALT)).text_color(rgb(TEXT)))
.when(!active, |view| view.text_color(rgb(MUTED)))
.hover(|view| view.bg(rgb(PANEL_ACTIVE)).text_color(rgb(TEXT)))
.child(name)
.when(active, |view| {
view.child(
div()
.mt_1()
.text_xs()
.text_color(rgb(MUTED))
.child("main · metal"),
)
})
.on_click(cx.listener(move |shell, _, window, cx| {
shell.select_worktree(index, window, cx);
}))
}))
.child( .child(
div() div()
.mt_3() .mt_3()
@@ -2460,23 +2487,7 @@ impl Render for LumbridgeShell {
shell.attach_panel(pane, window, cx); shell.attach_panel(pane, window, cx);
})) }))
})) }))
.child(div().flex_1()) .child(div().flex_1());
.child(
div()
.m_3()
.p_3()
.rounded(px(5.0))
.bg(rgb(PANEL_ALT))
.text_xs()
.text_color(rgb(MUTED))
.child("Buzz · lumbridgecode")
.child(
div()
.mt_1()
.text_color(rgb(SUCCESS))
.child("connected · signed identity"),
),
);
let tabs = div() let tabs = div()
.flex() .flex()
@@ -2499,7 +2510,7 @@ impl Render for LumbridgeShell {
.border_b_2() .border_b_2()
.border_color(rgb(ACCENT)) .border_color(rgb(ACCENT))
.text_sm() .text_sm()
.child(WORKSPACES[self.active_worktree]), .child("Lumbridge Code"),
) )
.child(div().flex_1()) .child(div().flex_1())
.child(div().px_3().text_xs().text_color(rgb(MUTED)).child(format!( .child(div().px_3().text_xs().text_color(rgb(MUTED)).child(format!(
@@ -2627,15 +2638,21 @@ impl Render for LumbridgeShell {
.ml_3() .ml_3()
.text_sm() .text_sm()
.text_color(rgb(MUTED)) .text_color(rgb(MUTED))
.child(format!("{} / main", WORKSPACES[self.active_worktree])), .child("Lumbridge Code"),
) )
.child(div().flex_1()) .child(div().flex_1())
// The same tone rule runtime_rows applies: nothing live is
// not a success, and "0/5 LIVE PTYS" in green said it was.
.child( .child(
div() div()
.mr_4() .mr_4()
.text_xs() .text_xs()
.text_color(rgb(SUCCESS)) .text_color(rgb(if running_runtime_count == 0 {
.child(format!("metal · {runtime_summary}")), MUTED
} else {
SUCCESS
}))
.child(runtime_summary),
) )
.child( .child(
div() div()
@@ -2921,6 +2938,10 @@ mod tests {
} }
#[test] #[test]
#[allow(
clippy::unreadable_literal,
reason = "six-digit colour hex reads whole"
)]
fn xterm_color_cube_and_grayscale_are_deterministic() { fn xterm_color_cube_and_grayscale_are_deterministic() {
assert_eq!(indexed_terminal_color(16), 0x000000); assert_eq!(indexed_terminal_color(16), 0x000000);
assert_eq!(indexed_terminal_color(231), 0xffffff); assert_eq!(indexed_terminal_color(231), 0xffffff);
+32 -47
View File
@@ -36,18 +36,26 @@ impl PanelKind {
pub(crate) const fn description(self) -> &'static str { pub(crate) const fn description(self) -> &'static str {
match self { match self {
Self::Terminal => "A real local shell with its own runtime actor", Self::Terminal => "A real local shell with its own runtime actor",
Self::Browser => "An isolated system-web-engine surface", // Not a web engine. There is no browser in this build; the panel
// renders a placeholder surface and says so.
Self::Browser => "A placeholder surface. No web engine is embedded yet",
Self::Markdown => "Local notes, plans, and architecture", Self::Markdown => "Local notes, plans, and architecture",
Self::Review => "Repository changes and approval boundaries", Self::Review => "Repository changes and approval boundaries",
} }
} }
/// What a newly created panel is pointed at.
///
/// Every one of these used to name a specific machine — this developer's,
/// as it happens. A panel's target is whatever it was actually launched
/// against, and a new panel has been launched against nothing but the
/// local shell.
const fn default_target(self) -> &'static str { const fn default_target(self) -> &'static str {
match self { match self {
Self::Terminal => "metal · local runtime", Self::Terminal => "local shell",
Self::Browser => "isolated system web engine", Self::Browser => "no surface attached",
Self::Markdown => "lumbridge-code · local document", Self::Markdown => "local document",
Self::Review => "lumbridge-code · working tree", Self::Review => "working tree",
} }
} }
} }
@@ -96,49 +104,26 @@ pub(crate) struct PanelRegistry {
impl Default for PanelRegistry { impl Default for PanelRegistry {
fn default() -> Self { fn default() -> Self {
Self::seeded() Self::first_run()
} }
} }
impl PanelRegistry { impl PanelRegistry {
pub(crate) fn seeded() -> Self { /// The workspace a first run opens with.
///
/// Three real local shells and nothing else. This used to seed six panes
/// announcing "Codex · runtime / metal · Tailscale SSH", "Claude Code · UI
/// / `MacBook` Air · local" and a Pi pane on a saved host — none of which
/// existed. Every one was a `/bin/sh`. A pane claims a harness when one has
/// actually been launched into it, and until then it is a terminal.
///
/// Three, not one: `visible_panel_count` branches at 1100 px and 2800 px,
/// so a single-panel default renders one pane on an ultrawide.
pub(crate) fn first_run() -> Self {
let seeds = [ let seeds = [
( (PanelKind::Terminal, "Terminal 1", "local shell", None),
PanelKind::Terminal, (PanelKind::Terminal, "Terminal 2", "local shell", None),
"Codex · runtime", (PanelKind::Terminal, "Terminal 3", "local shell", None),
"metal · Tailscale SSH",
SeedPane::CodexRuntime,
),
(
PanelKind::Terminal,
"Claude Code · UI",
"MacBook Air · local",
SeedPane::ClaudeUi,
),
(
PanelKind::Terminal,
"Pi · docs",
"amd-server · OpenSSH",
SeedPane::PiDocs,
),
(
PanelKind::Markdown,
"Architecture.md",
"lumbridge-code · worktree",
SeedPane::Architecture,
),
(
PanelKind::Browser,
"Preview · ACP docs",
"isolated system web engine",
SeedPane::AcpPreview,
),
(
PanelKind::Review,
"Changes · lumbridge-runtime",
"metal · worktree remote-runtime",
SeedPane::RuntimeReview,
),
]; ];
let panels = seeds let panels = seeds
.into_iter() .into_iter()
@@ -149,7 +134,7 @@ impl PanelRegistry {
title: title.to_owned(), title: title.to_owned(),
target: target.to_owned(), target: target.to_owned(),
attached: index < 5, attached: index < 5,
seed: Some(seed), seed,
}) })
.collect(); .collect();
Self { Self {
@@ -335,7 +320,7 @@ mod tests {
#[test] #[test]
fn dynamic_panels_receive_monotonic_ids_and_insert_beside_selection() { fn dynamic_panels_receive_monotonic_ids_and_insert_beside_selection() {
let mut registry = PanelRegistry::seeded(); let mut registry = PanelRegistry::first_run();
let browser = registry.create(PanelKind::Browser); let browser = registry.create(PanelKind::Browser);
let terminal = registry.create(PanelKind::Terminal); let terminal = registry.create(PanelKind::Terminal);
assert_eq!(browser, PanelId(7)); assert_eq!(browser, PanelId(7));
@@ -347,7 +332,7 @@ mod tests {
#[test] #[test]
fn identity_and_detached_state_round_trip_without_reuse() { fn identity_and_detached_state_round_trip_without_reuse() {
let mut registry = PanelRegistry::seeded(); let mut registry = PanelRegistry::first_run();
let created = registry.create(PanelKind::Markdown); let created = registry.create(PanelKind::Markdown);
assert!(registry.detach(created)); assert!(registry.detach(created));
let json = registry.to_json().unwrap(); let json = registry.to_json().unwrap();
@@ -358,7 +343,7 @@ mod tests {
#[test] #[test]
fn detach_preserves_panel_and_reattach_restores_same_identity() { fn detach_preserves_panel_and_reattach_restores_same_identity() {
let mut registry = PanelRegistry::seeded(); let mut registry = PanelRegistry::first_run();
let id = registry.create(PanelKind::Terminal); let id = registry.create(PanelKind::Terminal);
assert!(registry.detach(id)); assert!(registry.detach(id));
assert!(registry.panel(id).is_some()); assert!(registry.panel(id).is_some());
+59 -25
View File
@@ -34,9 +34,15 @@ struct DeclaredProfile {
account: &'static str, account: &'static str,
} }
/// The Codex pane maps onto the probe's own primary-window profile, so a live /// Only harnesses with a real adapter behind them.
/// reading lands under the pane that produced it. The rest are declarations ///
/// with no adapter behind them yet. /// A third entry declared "Pi · spark-1 · laguna-s-2.1 · self-hosted" with no
/// probe of any kind, so the strip carried a permanent empty rail for a harness
/// the user was not running. Declaring a profile is a promise that the gap is
/// real; declaring one nothing can ever fill is just a fabricated row.
///
/// The seed mapping stays: it is how a live reading lands under the pane that
/// produced it, once a harness can actually be launched into a pane.
const DECLARED: &[DeclaredProfile] = &[ const DECLARED: &[DeclaredProfile] = &[
DeclaredProfile { DeclaredProfile {
seed: SeedPane::CodexRuntime, seed: SeedPane::CodexRuntime,
@@ -58,14 +64,6 @@ const DECLARED: &[DeclaredProfile] = &[
model: "five-hour window", model: "five-hour window",
account: "subscription", account: "subscription",
}, },
DeclaredProfile {
seed: SeedPane::PiDocs,
id: "spark-1-local",
harness: "Pi",
provider: "spark-1",
model: "laguna-s-2.1",
account: "self-hosted",
},
]; ];
pub(crate) struct UsageFeed { pub(crate) struct UsageFeed {
@@ -78,8 +76,45 @@ pub(crate) struct UsageFeed {
probe_status: String, probe_status: String,
} }
/// Which probes to start.
///
/// Passed in rather than read from the environment inside the constructor:
/// a test that wanted a probe-free feed used to set a process-wide environment
/// variable, which required `unsafe` and silently disabled both probes for
/// every other test in the binary.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct UsageFeedOptions {
pub(crate) codex: bool,
pub(crate) claude: bool,
}
impl UsageFeedOptions {
/// Reads the documented opt-out switches. The only place the environment
/// is consulted, so a caller can always ask for something else.
fn from_env() -> Self {
let enabled = |name: &str| std::env::var_os(name).is_none_or(|value| value != "0");
Self {
codex: enabled("LUMBRIDGE_CODEX_PROBE"),
claude: enabled("LUMBRIDGE_CLAUDE_PROBE"),
}
}
/// Declares every profile and starts nothing.
#[cfg(test)]
pub(crate) const fn none() -> Self {
Self {
codex: false,
claude: false,
}
}
}
impl UsageFeed { impl UsageFeed {
pub(crate) fn start() -> Self { pub(crate) fn start() -> Self {
Self::start_with(UsageFeedOptions::from_env())
}
pub(crate) fn start_with(options: UsageFeedOptions) -> Self {
let mut profiles = BTreeMap::new(); let mut profiles = BTreeMap::new();
let mut seeds = Vec::new(); let mut seeds = Vec::new();
for declared in DECLARED { for declared in DECLARED {
@@ -105,8 +140,8 @@ impl UsageFeed {
health: BTreeMap::new(), health: BTreeMap::new(),
probe_status: "no usage adapter running".to_owned(), probe_status: "no usage adapter running".to_owned(),
}; };
feed.start_codex_probe(); feed.start_codex_probe(options.codex);
feed.start_claude_probe(); feed.start_claude_probe(options.claude);
feed feed
} }
@@ -115,8 +150,8 @@ impl UsageFeed {
/// A failure here is not an error state for the shell: the profiles stay /// A failure here is not an error state for the shell: the profiles stay
/// declared and render as unavailable, which is exactly what they should do /// declared and render as unavailable, which is exactly what they should do
/// when no adapter can run. /// when no adapter can run.
fn start_codex_probe(&mut self) { fn start_codex_probe(&mut self, enabled: bool) {
if std::env::var_os("LUMBRIDGE_CODEX_PROBE").is_some_and(|value| value == "0") { if !enabled {
self.probe_status = "codex probe disabled".to_owned(); self.probe_status = "codex probe disabled".to_owned();
return; return;
} }
@@ -143,8 +178,12 @@ impl UsageFeed {
/// This one reads files rather than launching anything, so it has no /// This one reads files rather than launching anything, so it has no
/// harness to fail to find — a missing projects directory simply reports /// harness to fail to find — a missing projects directory simply reports
/// nothing until Claude Code creates it. /// nothing until Claude Code creates it.
fn start_claude_probe(&mut self) { fn start_claude_probe(&mut self, enabled: bool) {
if std::env::var_os("LUMBRIDGE_CLAUDE_PROBE").is_some_and(|value| value == "0") { if !enabled {
// Say so, rather than leaving whatever the Codex path last wrote.
// A disabled probe that reports nothing about itself is
// indistinguishable from one that failed to start.
self.probe_status = "claude probe disabled".to_owned();
return; return;
} }
match ClaudeCodeProbe::start(ClaudeCodeProbeOptions::default()) { match ClaudeCodeProbe::start(ClaudeCodeProbeOptions::default()) {
@@ -415,19 +454,14 @@ pub(crate) struct UsageSegment {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{DECLARED, UsageFeed}; use super::{DECLARED, UsageFeed, UsageFeedOptions};
use crate::panel_registry::SeedPane; use crate::panel_registry::SeedPane;
use lumbridge_core::UsageProvenance; use lumbridge_core::UsageProvenance;
/// Never starts a probe, so the test cannot depend on an installed harness, /// Never starts a probe, so the test cannot depend on an installed harness,
/// on the user's transcripts, or on a status-line bridge being present. /// on the user's transcripts, or on a status-line bridge being present.
fn declared_only_feed() -> UsageFeed { fn declared_only_feed() -> UsageFeed {
// SAFETY-FREE: this only sets environment variables for this process. UsageFeed::start_with(UsageFeedOptions::none())
unsafe {
std::env::set_var("LUMBRIDGE_CODEX_PROBE", "0");
std::env::set_var("LUMBRIDGE_CLAUDE_PROBE", "0");
}
UsageFeed::start()
} }
#[test] #[test]
@@ -485,7 +519,7 @@ mod tests {
let strip = feed.strip(); let strip = feed.strip();
assert_eq!(strip.len(), DECLARED.len()); assert_eq!(strip.len(), DECLARED.len());
let labels: Vec<&str> = strip.iter().map(|segment| segment.label.as_str()).collect(); let labels: Vec<&str> = strip.iter().map(|segment| segment.label.as_str()).collect();
assert_eq!(labels, ["CODEX", "CLAUDE CODE", "PI"]); assert_eq!(labels, ["CODEX", "CLAUDE CODE"]);
for segment in &strip { for segment in &strip {
assert!( assert!(
segment.headline.is_none() && segment.reset.is_none(), segment.headline.is_none() && segment.reset.is_none(),
+8 -18
View File
@@ -198,25 +198,15 @@ pub const PANES: [PaneFixture; 6] = [
}, },
]; ];
pub const WORKSPACES: [&str; 5] = [ /// The one footer string the frozen Floem shell renders.
"Lumbridge Code",
"Runtime / metal",
"UI spikes",
"ACP adapters",
"Usage telemetry",
];
/// Static footer strings retained only by the Floem comparison shell.
/// ///
/// These are layout fixtures, not usage. They are not a usage source and must /// It replaces three that read like data — a pane census, "Codex · ChatGPT
/// not be rendered as one: the GPUI shell now derives its footer from /// subscription · 62% window remaining", and "burn 8.4%/hr · resets in 2h 14m".
/// `lumbridge_core::UsageLedger`, where every value carries a provenance and a /// Decision 0013 names that exact form as the thing that must never be shown,
/// missing fact renders as missing. Delete these once Floem consumes the ledger /// and a comparison shell rendering a fabricated quota beside a real one is
/// or the candidate is retired. See decision 0012. /// worse than a comparison shell with no footer numbers at all. The GPUI shell
pub const FOOTER_LEFT: &str = "6 panes · 3 remote · 1 needs input"; /// derives its footer from `lumbridge_core::UsageLedger`. See decision 0012.
/// See [`FOOTER_LEFT`]: a layout fixture, not a usage reading. pub const FOOTER_FIXTURE_NOTICE: &str = "layout fixture · no usage source";
pub const FOOTER_CENTER: &str = "Codex · ChatGPT subscription · 62% window remaining";
/// See [`FOOTER_LEFT`]: a layout fixture, not a usage reading.
pub const FOOTER_RIGHT: &str = "burn 8.4%/hr · resets in 2h 14m";
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct PaneState { pub struct PaneState {