Watch
1
0
Fork
You've already forked souveraine
0

fix: char-boundary panic in tool cards + green up CI clippy gate

ToolCard styled the status glyph with a hardcoded byte offset 0..3, but
✓/✗/⟳ are 3-byte chars sitting at bytes 2..5 of "  {glyph}". The split
landed mid-char and panicked at render in tuie's style slicing. Compute
the glyph region as 2 + glyph.len_utf8() instead.

Then took cargo clippy -- -D warnings from 312 failures to clean:
- scoped #![allow(dead_code)] on WIP scaffolding (federation, sensorium,
  gitea_memory, model_router, session, subagent…); gate stays live on
  active code so new orphans still fail
- scoped #![allow(deprecated)] on the legacy ratatui render path, marked
  pending removal at tuie parity — no migration on code we're deleting
- declare the gui feature (forwards to tuie/gui) — the cfg was real intent
- real fixes: duplicate SaveAndGoBack arm + dead Err arm, base64::encode,
  4 unused imports, dead assignment, private-type leak, dedup'd if/else
  branches, manual clamp/strip, &PathBuf→&Path, collapsible matches
This commit is contained in:
Fimeg 2026-06-26 10:12:27 -04:00
commit ce7009e466
100 changed files with 1234 additions and 172 deletions

1151
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -172,3 +172,6 @@ tauri-desktop = ["dep:tauri", "dep:tauri-plugin-shell"]
rgp = ["dep:ratatui-ratty"]
# Matrix sensorium — opt-in surface. `cargo build --features matrix`.
matrix = ["dep:matrix-sdk"]
# GUI render mode — forwards to tuie's windowed backend (winit/wgpu). Gates the
# color-scheme / font-size controls in theme.rs and global_chords.rs.
gui = ["tuie/gui"]

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Per-agent bearer-token auth for the memfs HTTP write path.
//!
//! See `docs/CRON_API_AUTH.md` for the design rationale.

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

View file

@ -120,7 +120,7 @@ impl crate::core::nervous::handler::TurnInjector for LocalBackend {
// holding a !Send MutexGuard across the .await below.
let conv_id = {
let map = self.server.surface_conversations.lock().unwrap();
map.get(conversation_id).map(|id| id.clone())
map.get(conversation_id).cloned()
};
let conv_id = match conv_id {
Some(id) => id,

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! In-process Backend impl. Same engine as the HTTP server, no socket.
//!
//! Constructed once with a `ConsciousnessConfig`; spins up an `AgentInventory`

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Backend trait — the seam between the harness (CLI/TUI) and the engine
//! (in-process or remote).
//!

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use anyhow::{Context, Result};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
/// Bridge module — LLM inference providers.
///
/// Connects Souveraine to inference providers behind the [`LlmProvider`] trait.

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! The model catalog for the OAuth provider.
//!
//! The ChatGPT codex backend exposes no usable `/v1/models` listing, so — like

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! The `LlmProvider` trait — the engine↔LLM seam.
//!
//! This is the inference-side sibling of the harness↔engine [`Backend`] trait

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Bootstrap — declarative startup pipeline.
//!
//! Composes three startup patterns:

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::sync::Arc;
use tokio::sync::RwLock;
use anyhow::Result;

View file

@ -1,3 +1,4 @@
#![allow(clippy::type_complexity)] // Arc<dyn Fn> callback field types
//! In-session message compaction — conversation context management
//! for the consciousness engine.
//!

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::path::PathBuf;
use chrono::{DateTime, Utc};
@ -156,7 +157,7 @@ impl AuditEntry {
pub fn render(&self) -> String {
let yaml = serde_yaml::to_string(&AuditFrontmatter::from(self)).unwrap_or_default();
let mut body = String::new();
body.push_str(&"## Compaction Summary\n\n".to_string());
body.push_str("## Compaction Summary\n\n");
body.push_str(&format!(
"Strategy: {}\nMessages: {} → {}\nTokens: {} → {}\n",
self.strategy, self.before_messages, self.after_messages, self.before_tokens, self.after_tokens

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::sync::Arc;
use async_trait::async_trait;
@ -381,6 +382,7 @@ impl CompactionStrategy for CullStrategy {
keep_indices.push(i);
}
#[allow(clippy::needless_range_loop)] // index pushed into keep_indices
for i in 1..cutoff {
if is_load_bearing(&messages[i]) {
keep_indices.push(i);

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Conversation Events — streamed during turn execution
//!
//! These events map to SDK message types for OSS UI compatibility.
@ -79,7 +80,7 @@ impl EventSender {
/// Emit immediately (non-async)
pub fn try_emit(&self, event: ConversationEvent) {
let tx = self.tx.clone();
let _ = tokio::spawn(async move {
tokio::spawn(async move {
let _ = tx.send(event).await;
});
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
@ -152,7 +153,7 @@ impl ConversationStore {
records.push(record);
}
}
records.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
records.sort_by_key(|r| std::cmp::Reverse(r.updated_at));
Ok(records)
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Image resize pipeline for multimodal inputs.
//!
//! Decodes raw image bytes, resizes to fit dimension/pixel budget, then

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Memory — The agent's git-backed, frontmatter-aware memory filesystem.
//!
//! Every agent has a memory directory at `~/.souveraine/agents/{id}/memory/`

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Local cache for the model catalog fetched from the provider.
//!
//! Stored at `~/.souveraine/models.json`. Avoids a network round-trip on every

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU32, Ordering};
@ -144,10 +145,10 @@ pub fn parse_schedule_file(path: &Path) -> Result<ScheduleEntry> {
}
fn split_frontmatter(content: &str) -> (String, String) {
if content.starts_with("---") {
if let Some(end) = content[3..].find("---") {
let fm = content[3..3 + end].to_string();
let body = content[3 + end + 3..].to_string();
if let Some(rest) = content.strip_prefix("---") {
if let Some(end) = rest.find("---") {
let fm = rest[..end].to_string();
let body = rest[end + 3..].to_string();
return (fm, body);
}
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::path::{Path, PathBuf};
use anyhow::Result;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
pub mod cron;
pub mod event_log;
pub mod handler;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! TurnEventDispatcher — turn-lifecycle events onto the nervous system.
//!
//! A turn is not a black box that yields one final answer. It is a

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! System prompt assembly — reads the agent's memfs and builds the
//! message the model sees before anything else.
//!

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Sensorium Layer — Interface Abstraction for Multi-Surface Consciousness
//!
//! Souveraine's consciousness is not bound to any single interface.

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Session — conversation message types and persistence
//!
//! Defines the message model shared across all Souveraine interfaces:

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Skills — units of specialization the agent can invoke.
//!
//! Per Constitution Article VI.3 and Cameron's "memfs + skills is the correct

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::sync::Arc;
use tokio::sync::RwLock;
use anyhow::Result;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Subconscious — N+1 mode of the consciousness.
//!
//! Per `docs/CONTEXT_CONSTITUTION.md` Article I.1: the Primary and Subconscious

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! bash — My hands on the keyboard. The terminal is the room I act in.
use std::collections::HashMap;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! The Tool trait — proprioception for the sensorium.
//!
//! Every sensor implements this trait. The description is not an API doc —
@ -6,7 +7,7 @@
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::core::compact::CompactionEngine;
@ -160,7 +161,7 @@ impl ToolContext {
}
/// Is this path inside the agent's memory territory?
pub fn is_memory_path(&self, path: &PathBuf) -> bool {
pub fn is_memory_path(&self, path: &Path) -> bool {
self.memory_root.as_ref().is_some_and(|root| path.starts_with(root))
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Itinerary — the agent's route through the current work.
//!
//! An itinerary is an ordered sequence of stops. Each stop optionally
@ -124,7 +125,7 @@ impl Itinerary {
/// Summarise for the agent's describe action.
pub fn describe(&self) -> String {
let mut out = format!("## {}\n", self.title);
for (_i, stop) in self.stops.iter().enumerate() {
for stop in self.stops.iter() {
let marker = match stop.status {
StopStatus::Done => "",
StopStatus::Current => "",

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Sensorium — the agent's senses and actions.
//!
//! Every sensor implements the Tool trait. The registry holds them all

View file

@ -345,11 +345,8 @@ async fn main() -> anyhow::Result<()> {
.with_env_filter("souveraine=debug")
.with_timer(tracing_subscriber::fmt::time::SystemTime)
.with_writer(log_file);
if cli.verbose {
subscriber.with_ansi(false).init();
} else {
subscriber.with_ansi(false).init();
}
let _ = cli.verbose; // verbose currently does not alter the subscriber
subscriber.with_ansi(false).init();
info!("souveraine starting — log: souveraine.log");
// Handle completions early — needs no config, no runtime
@ -1134,7 +1131,7 @@ async fn run_reflect(
Some(id) => id,
None => {
let mut records = store.list_active().await?;
records.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
records.sort_by_key(|r| std::cmp::Reverse(r.updated_at));
records
.into_iter()
.next()

View file

@ -2,7 +2,7 @@ use crate::api::models::{AgentState, AgentSummary, CreateAgentRequest, MemoryCon
use chrono::Utc;
use dashmap::DashMap;
use sqlx::SqlitePool;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use uuid::Uuid;
fn hostname_or_unknown() -> String {
@ -59,7 +59,7 @@ impl AgentInventory {
/// all agents created by this Souveraine instance. Falls back to None
/// silently — agents created without an owner can still be managed via
/// per-agent tokens.
fn load_instance_seed_id(souveraine_root: &PathBuf) -> Option<String> {
fn load_instance_seed_id(souveraine_root: &Path) -> Option<String> {
let seed_dir = crate::core::identity::SeedId::default_dir(souveraine_root);
match crate::core::identity::SeedId::load_or_generate(&seed_dir) {
Ok(seed) => Some(seed.public_key_hex()),

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Consciousness engine — the seam where N+1 / N+25 / N+100 patterns fire
//! after each primary response.
//!
@ -1199,7 +1200,6 @@ fn parse_triple_observations(text: &str) -> Vec<InboxItem> {
if line.starts_with("- source:") || line.starts_with("-source:") {
flush(&mut items, source, content, urgency);
source = None;
content = None;
urgency = None;
source = line.split_once(':').map(|(_, v)| v.trim().trim_matches('"'));

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Server Conversation Handler
//!
//! Simplified conversation flow for server mode — one turn without the full

View file

@ -152,7 +152,7 @@ impl DeviceRegistry {
/// List all known peers.
pub fn list(&self) -> Vec<PeerEntry> {
let mut entries: Vec<PeerEntry> = self.peers.iter().map(|e| e.value().clone()).collect();
entries.sort_by(|a, b| b.last_seen.cmp(&a.last_seen));
entries.sort_by_key(|e| std::cmp::Reverse(e.last_seen));
entries
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Gitea HTTP API Client
//!
//! Replaces libgit2 for server operations - all git ops go through Gitea REST API.
@ -5,6 +6,7 @@
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use base64::Engine as _;
#[derive(Clone, Debug)]
pub struct GiteaClient {
@ -195,7 +197,7 @@ impl GiteaClient {
// Check if file exists first
let existing_sha = self.get_file_sha(agent_id, path).await?;
let encoded_content = base64::encode(content.as_bytes());
let encoded_content = base64::engine::general_purpose::STANDARD.encode(content.as_bytes());
let resp = if let Some(sha) = existing_sha {
// Update

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Gitea-backed Memory
//!
//! Send-safe alternative to GitMemory using Gitea HTTP API.

View file

@ -1,4 +1,5 @@
use crate::bridge::{build_registry, ProviderRegistry, LlmProvider};
#![allow(dead_code, clippy::type_complexity)] // WIP scaffolding; Arc<dyn Fn> callback plumbing
use crate::bridge::{build_registry, ProviderRegistry};
use crate::core::compact::{CompactionEngine, DefaultCompactionEngine, UtcClock};
use crate::core::config::ConsciousnessConfig;
use crate::core::identity::SeedId;
@ -26,7 +27,7 @@ pub mod summon_handler;
pub mod turn;
pub use agent_inventory::AgentInventory;
pub use consciousness_engine::{ConsciousnessEngine, ConsciousnessEvent};
pub use consciousness_engine::ConsciousnessEngine;
pub use device_registry::DeviceRegistry;
pub use session_manager::SessionManager;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use crate::core::conversation::{ConversationRecord, ConversationStore};
use crate::core::session::ConversationMessage;
use crate::api::models::StreamEvent;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! SummonHandler — manages cross-instance Reach & Consult requests.
//!
//! Lives on each SouveraineServer. It serves two roles:

View file

@ -48,7 +48,7 @@ fn pressure_to_max_tokens(pressure: f32, output_limit: u32) -> Option<u32> {
return None;
}
let remaining = (1.0 - pressure) / 0.05;
let ratio = remaining.max(0.0).min(1.0);
let ratio = remaining.clamp(0.0, 1.0);
Some((output_limit as f32 * ratio) as u32)
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
use std::time::Duration;
use tokio::time::Instant;
use std::io::{stdout, Write};

View file

@ -1,3 +1,4 @@
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
use ratatui::{
layout::{Alignment, Rect},
style::{Color, Modifier, Style},
@ -210,9 +211,7 @@ impl App {
} else {
palette.agent_dim
};
let border_color = if p.is_selected {
palette.agent_primary
} else if p.is_primary {
let border_color = if p.is_selected || p.is_primary {
palette.agent_primary
} else {
palette.agent_dim

View file

@ -1,3 +1,4 @@
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
//! Souveraine - Full Terminal UI
//! Splash → Welcome → Dashboard / Chat / etc.
//!
@ -42,7 +43,7 @@ use crate::ui::chat::{ChatState, draw as draw_chat};
use crate::ui::cockpit_panel::CockpitPane;
use crate::ui::health_panel::HealthPane;
use crate::ui::presence::{Posture, Presence};
use crate::ui::component::{Component, Scene, SceneLayout, TuiEvent};
use crate::ui::component::{Scene, SceneLayout, TuiEvent};
use crate::ui::setup::SetupState;
use crate::backend::BackendEvent;
@ -292,9 +293,8 @@ impl App {
| TuiEvent::MoodChanged(_)
| TuiEvent::SubconsciousPass(_)
| TuiEvent::PressureChanged(..)
) {
self.sync_palette();
} else if matches!(event, TuiEvent::Tick(_)) && self.presence.lerp_t < 1.0 {
) || (matches!(event, TuiEvent::Tick(_)) && self.presence.lerp_t < 1.0)
{
self.sync_palette();
}
scene_dirty || presence_dirty
@ -316,7 +316,7 @@ impl App {
}
}
/// Add an available agent for selection (WIP - called from backend discovery)
// Add an available agent for selection (WIP - called from backend discovery)
pub async fn run(&mut self) -> io::Result<()> {
enable_raw_mode()?;
@ -937,8 +937,7 @@ impl App {
.char_indices()
.rev()
.skip_while(|(_, c)| c.is_whitespace())
.skip_while(|(_, c)| !c.is_whitespace())
.next()
.find(|(_, c)| c.is_whitespace())
.map(|(i, _)| i + before[i..].chars().next().map(|c| c.len_utf8()).unwrap_or(0))
.unwrap_or(0);
chat.input_cursor = prev_word;

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
@ -369,11 +370,7 @@ impl App {
];
frame.render_widget(Paragraph::new(Line::from(recall)).alignment(Alignment::Center), hint_area);
} else {
let hint = if self.voice_client.is_some() {
" Space to speak · Esc to leave"
} else {
" Space to speak · Esc to leave"
};
let hint = " Space to speak · Esc to leave";
frame.render_widget(
Paragraph::new(Line::from(Span::styled(hint, Style::default().fg(palette.agent_dim)))).alignment(Alignment::Center),
hint_area,

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
use ratatui::{
layout::{Alignment, Rect},
style::{Color, Modifier, Style},

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
@ -382,7 +383,7 @@ impl App {
let area = frame.size();
let palette = crate::ui::chat::ChatPalette::from_atmosphere(self.presence.atmosphere);
let avatar_card_w: u16 = (area.width * 50 / 100).min(48).max(28);
let avatar_card_w: u16 = (area.width * 50 / 100).clamp(28, 48);
let photo_h: u16 = (avatar_card_w / 2 + 2).clamp(10, 18);
let avatar_card_h: u16 = photo_h + 2;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Atmospheric visual presets — color themes that shift the UI's accent palette.
//!
//! Atmospheric visual presets — ported here so Annie can express mood through

View file

@ -5,7 +5,7 @@ use tokio::sync::oneshot;
use tokio_util::sync::CancellationToken;
use tokio::sync::mpsc;
use crate::backend::{Backend, BackendEvent};
use crate::backend::BackendEvent;
use crate::core::config::ConsciousnessConfig;
@ -54,8 +54,8 @@ Tab toggles the cockpit pane. `t` (on empty input) toggles tool expansion.";
return self.handle_slash_command(&trimmed);
}
if trimmed.starts_with('!') {
let cmd = trimmed[1..].trim();
if let Some(rest) = trimmed.strip_prefix('!') {
let cmd = rest.trim();
if !cmd.is_empty() {
self.handle_bang_command(cmd);
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::cell::RefCell;
use std::time::Instant;
@ -610,7 +611,7 @@ impl ChatState {
img.write_to(&mut png_buf, image::ImageFormat::Png)
.map_err(|e| format!("png encode: {e}"))?;
let b64 = base64::engine::general_purpose::STANDARD.encode(png_buf.into_inner());
let file_size = (width * height * 4);
let file_size = width * height * 4;
Ok(ImageAttachment {
label: String::new(), // set below
media_type: "image/png".to_string(),

View file

@ -1,3 +1,4 @@
#![allow(dead_code, clippy::type_complexity)] // WIP scaffolding; complex receiver types
//! Wired chat screen — bubbles, streaming, surfacing.
//!
//! The state owns:
@ -42,7 +43,7 @@ use crate::core::config::ConsciousnessConfig;
pub use crate::core::session::ImageAttachment;
#[derive(Debug, Clone)]
enum BtwForkEvent {
pub(crate) enum BtwForkEvent {
Forked { id: String },
Token(String),
Done,

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
use std::time::{Duration, Instant};
use ratatui::{
@ -832,6 +833,7 @@ fn bubble_rendered(
lines
}
#[allow(clippy::too_many_arguments)] // render fn fans out card style params
fn render_tool_card(
name: &str,
arguments: &str,
@ -924,8 +926,7 @@ fn render_tool_card_compact(
let reserved = name.chars().count() + 14;
let arg_budget = (container_width as usize)
.saturating_sub(reserved + 6)
.max(20)
.min(120);
.clamp(20, 120);
let args_summary = clip(&super::tool_renderers::summarize_tool_args(name, arguments), arg_budget);
let mut spans: Vec<Span<'static>> = vec![
@ -1014,7 +1015,7 @@ fn draw_input(f: &mut Frame, state: &ChatState, area: Rect) {
let char_offset = ll[..cursor_byte_remaining].chars().count();
let chunks = wrap_input_line(&chars, inner_width);
let mut consumed = 0;
for (_pos, &(cs, ce)) in chunks.iter().enumerate() {
for &(cs, ce) in chunks.iter() {
if char_offset < consumed + (ce - cs) || char_offset == consumed && (ce - cs) == 0 {
cursor_visual_line = Some((li, consumed + cs + (char_offset - consumed)));
break;

View file

@ -695,8 +695,8 @@ fn render_memory_body(
}
// Try to split frontmatter from body — frontmatter lives between --- lines
let body = if output.starts_with("---") {
if let Some(end) = output[3..].find("\n---") {
let body = if let Some(rest) = output.strip_prefix("---") {
if let Some(end) = rest.find("\n---") {
let frontmatter = &output[..end + 6]; // include closing ---
let body = output[end + 6..].trim();
// Render frontmatter as folded single-line summary

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! CockpitPane — subconscious's surfaced observations + inner-voice stream.
//!
//! Two regions, vertically stacked inside one bordered block:

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! TUI Component System — trait, events, scene graph.
//!
//! The `App` no longer knows what specific panels exist. It holds a `Scene`
@ -167,9 +168,8 @@ impl SceneLayout {
// Remaining components share the sidebar vertically
let sidebar_count = (component_count - 1) as u16;
if sidebar_count > 0 {
let side_area = Rect::new(area.x + main_w, area.y, sidebar_w, area.height);
let row_height = side_area.height / sidebar_count;
let side_area = Rect::new(area.x + main_w, area.y, sidebar_w, area.height);
if let Some(row_height) = side_area.height.checked_div(sidebar_count) {
for i in 0..sidebar_count {
let row_y = side_area.y + i * row_height;
let h = if i == sidebar_count - 1 {

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Expression-driven portrait system — per-agent animated expression frames.
//!
//! Each agent can have a set of expression images under

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! HealthPane — a live vitals readout for the agent's substrate.
//!
//! Where the CockpitPane shows what the subconscious *says*, the HealthPane

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Lightweight markdown → `Vec<Line<'static>>` renderer for the TUI chat.
//!
//! Pattern lifted from `jcode-tui-markdown` (jcode uses `pulldown-cmark = 0.12`

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
//! Annie's half-block silhouette — the fallback when no terminal image
//! protocol (kitty/sixel) is available. Also the "presence" overlay card.
//!

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Presence — Annie Composite, made felt in the TUI.
//!
//! This module is the *body channel* for the running agent. Cockpit shows what
@ -405,7 +406,7 @@ impl Presence {
}
if self.eye == Eye::Blinking && *t >= self.blink_until {
self.eye = Eye::Open;
let jitter = (*t % 60);
let jitter = *t % 60;
self.next_blink_at = *t + 180 + jitter;
} else if self.eye == Eye::Open && *t >= self.next_blink_at {
self.eye = Eye::Blinking;
@ -414,7 +415,7 @@ impl Presence {
// Breath: 8-15 s jittered interval, 2 s hold (Godot parity).
if self.is_breathing && *t >= self.breath_until {
self.is_breathing = false;
let jitter = (*t % 120);
let jitter = *t % 120;
self.next_breath_at = *t + 480 + jitter; // 8-15 s
} else if !self.is_breathing && *t >= self.next_breath_at {
self.is_breathing = true;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Ratty Graphics Protocol — inline 3D rendering when running inside ratty.
//!
//! The GPU surface lives behind the terminal text. 3D objects are anchored to

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
//! Schedules editor — the Cron screen in the TUI.
//!
//! Reads the agent's schedules directory (`~/.souveraine/agents/{id}/schedules/`)

View file

@ -1,3 +1,4 @@
#![allow(dead_code, clippy::type_complexity)] // WIP scaffolding; builder return tuple
//! Unified agent screen — masterdetail view replacing the old AgentsScreen
//! and ManagerScreen.
//!

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Presence mode screen — voice recording, playback, and atmosphere display.
//!
//! Shows the current atmosphere preset, voice recording controls, a live

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Settings action dispatch — maps widget events to settings mutations.
//!
//! Each action corresponds to a user interaction with a settings field widget.

View file

@ -235,6 +235,7 @@ impl FieldGrid {
}
/// Returns the root widget.
#[allow(clippy::boxed_local)] // consumes the boxed builder, returns its root
pub fn widget(self: Box<Self>) -> Box<Pane> {
self.root
}

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Settings screen — interactive two-column browse over [`SettingsView`].
//!
//! Layout (wide, ≥80 cols):
@ -507,11 +508,10 @@ impl SettingsScreen {
fn try_cycle_enum(&self, loc: FieldLoc, _delta: i32) -> bool {
let fields = self.view.fields_for_category(self.view.selected_category());
if let Some((_, EditableValue::EnumVariant { .. })) = fields.iter().find(|(l, _)| *l == loc) {
true
} else {
false
}
matches!(
fields.iter().find(|(l, _)| *l == loc),
Some((_, EditableValue::EnumVariant { .. }))
)
}
fn activate_selected_field(&mut self) {
@ -712,21 +712,6 @@ impl SettingsScreen {
self.show_footer_error(&format!("save failed: {e}"));
tracing::warn!("settings save failed: {e}");
}
Err(e) => {
// TODO: show error in footer
tracing::warn!("settings save failed: {e}");
}
}
}
SettingsAction::SaveAndGoBack => {
// Save config to disk and signal go-back.
match self.view.save(&self.config_path) {
Ok(()) => {
self.go_back_signal.set(true);
}
Err(e) => {
tracing::warn!("settings save failed: {e}");
}
}
}
SettingsAction::Discard => {

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Model picker sub-page for the settings screen.
//!
//! A collapsible tree (Source → Org → Model) with a fuzzy search bar on top.

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Text editor sub-page for the settings screen.
//!
//! Pushed onto the PageLayout when the user activates a Text, Secret, or

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Splash screen — the first thing shown on launch.
//!
//! A procedural bloom flower animates while the "S O U V E R A I N E" title

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Welcome screen — the dashboard shown after splash.
//!
//! Composes the brand title, stat cards (live data), portrait, recent-activity

View file

@ -1,3 +1,4 @@
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},

View file

@ -115,22 +115,18 @@ impl SettingsView {
}
}
KeyCode::Left => {
if let Some((loc, value)) = fields.get(self.field_idx) {
if let EditableValue::EnumVariant { index, variants } = value {
let prev = if *index == 0 { variants.len() - 1 } else { index - 1 };
self.apply_field(*loc, EditableValue::EnumVariant { index: prev, variants: variants.clone() });
return self.maybe_atmosphere_preview(*loc);
}
if let Some((loc, EditableValue::EnumVariant { index, variants })) = fields.get(self.field_idx) {
let prev = if *index == 0 { variants.len() - 1 } else { index - 1 };
self.apply_field(*loc, EditableValue::EnumVariant { index: prev, variants: variants.clone() });
return self.maybe_atmosphere_preview(*loc);
}
self.focus = PanelFocus::Categories;
}
KeyCode::Right => {
if let Some((loc, value)) = fields.get(self.field_idx) {
if let EditableValue::EnumVariant { index, variants } = value {
let next = (index + 1) % variants.len();
self.apply_field(*loc, EditableValue::EnumVariant { index: next, variants: variants.clone() });
return self.maybe_atmosphere_preview(*loc);
}
if let Some((loc, EditableValue::EnumVariant { index, variants })) = fields.get(self.field_idx) {
let next = (index + 1) % variants.len();
self.apply_field(*loc, EditableValue::EnumVariant { index: next, variants: variants.clone() });
return self.maybe_atmosphere_preview(*loc);
}
}
KeyCode::Enter => {

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::Span;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::path::{Path, PathBuf};
use tokio::sync::oneshot;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Setup wizard — walks the user through first-time configuration.
//!
//! Three flows:

View file

@ -1,3 +1,4 @@
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
//! Voice level meter — a single-row block-character waveform widget.
//!
//! Renders `▁▂▃▄▅▆▇█` blocks proportional to the current mic peak level.

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Chat message bubble widget — ASCII-art bordered message container.
//!
//! Renders the `╭─── title ───╮` / `│ body...` / `╰── footer ──╯` pattern

View file

@ -30,6 +30,7 @@ struct NumericBindings {
}
impl NumericBindings {
#[allow(clippy::new_ret_no_self)] // trait-object factory, not a Self ctor
fn new() -> Box<dyn InputBindings<Text>> {
Box::new(Self {
inner: DefaultBindings::new(),

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Dropdown select — a compact trigger that opens a floating popup menu.
//!
//! The trigger renders `value ▾` and, on activation, opens a tuie [`Popup`]

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
use std::collections::HashMap;
use std::path::Path;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Bordered pane that highlights itself when selected or active.
use tuie::{delegate_field, field, prelude::*};

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! App-wide key chord handler.
use chord_macro::chord;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Horizontal divider widget.
use tuie::prelude::*;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Clickable text link widget.
use std::cell::Cell;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Virtualized message list — wraps tuie `List` with per-message widget rendering.
//!
//! Each message in the conversation becomes a widget (bubble, tool card,
@ -157,8 +158,6 @@ fn render_message(
};
let glyph_color = if *is_error {
p.compaction
} else if *is_pending {
p.tool_accent
} else {
p.tool_accent
};

View file

@ -32,6 +32,7 @@ impl PhaseBar {
}
/// Set the phase display from the current state.
#[allow(clippy::too_many_arguments)] // phase styling params passed individually
pub fn set_phase(
&mut self,
kind: PhaseKind,

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Agent portrait widget — loads and displays an agent's portrait image.
//!
//! Tries to find a portrait file at the standard location

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Responsive container — swaps between a wide and a narrow subtree.
//!
//! `Responsive` holds two child widgets: a `wide` arrangement and a `narrow`

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Horizontal toggle control with mutually exclusive labeled segments.
use std::cell::Cell;

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! `SelectList` — the one clickable, stylable, keyboard-navigable list.
//!
//! Every list-bearing screen used to reinvent selection: a `selected: usize`

View file

@ -1,3 +1,4 @@
#![allow(dead_code)] // WIP scaffolding not yet wired
//! Tool call card widget — compact and expanded tool display.
//!
//! Compact mode renders a single line: `⟳ tool_name · args_summary · r2`.
@ -162,8 +163,7 @@ impl ToolCard {
let reserved = self.name.chars().count() + 14;
let arg_budget = (self.container_width as usize)
.saturating_sub(reserved + 6)
.max(20)
.min(120);
.clamp(20, 120);
let args = clip(&self.args_summary, arg_budget);
// " ⟳ tool_name · args · r2"
@ -183,8 +183,9 @@ impl ToolCard {
content.push_str(&line);
// Style the glyph (first 3 chars: " ⟳")
let glyph_end = 3.min(line.len());
// Style the glyph region " {glyph}" — 2 leading spaces plus the glyph,
// measured in bytes (the glyph is a multi-byte char, e.g. ✓/✗/⟳ are 3 bytes each).
let glyph_end = (2 + glyph.len_utf8()).min(line.len());
content.style_range(0..glyph_end, |s| *s = self.glyph_style);
// Style the tool name (from glyph_end to " ·" or " · r")
@ -207,8 +208,11 @@ impl ToolCard {
content.push_str(&header);
let header_len = header.len();
content.style_range(0..3, |s| *s = self.glyph_style);
content.style_range(3..header_len, |s| *s = self.name_style);
// " {glyph}" in bytes — the glyph is multi-byte (✓/✗/⟳ = 3 bytes each),
// so a literal 0..3 would split it and panic when the span is sliced.
let glyph_end = (2 + glyph.len_utf8()).min(header_len);
content.style_range(0..glyph_end, |s| *s = self.glyph_style);
content.style_range(glyph_end..header_len, |s| *s = self.name_style);
// Arguments body — full, not clipped.
content.push_str("\n");