Watch
1
0
Fork
You've already forked souveraine
0

refactor: rename Aster -> subconscious across src/

Aster is Ani's personal subconscious-agent name, not a framework
concept. The codebase now refers to the N+1 pass and its components as
subconscious everywhere — const names, fn names, variables, comments,
the schedule source tag, the todo source enum, and UI strings.

Mechanical rename, no behaviour change. grep -i aster src/ now returns
only Faster-Whisper. reference/ and docs/ historical context untouched.
This commit is contained in:
Fimeg 2026-05-16 19:44:51 -04:00
commit 27d86fe4ca
18 changed files with 79 additions and 79 deletions

View file

@ -653,7 +653,7 @@ impl Backend for LocalBackend {
interject: crate::backend::InterjectionQueue,
) -> Result<BoxStream<'static, Result<BackendEvent>>> {
// Resolve the agent for this conversation, then drain her
// subconscious's intrusive box. Anything Aster queued after the
// subconscious's intrusive box. Anything subconscious queued after the
// last turn rides in on Casey's next message as `[ surfacing: ... ]`
// lines — the lettabot-v017 pattern, ported. This is the channel
// by which a Critical observation can interrupt mid-conversation
@ -733,7 +733,7 @@ impl crate::core::nervous::handler::TurnInjector for LocalBackend {
/// idle. We grab the most recent conversation (or create a fresh one
/// if the agent has none), append the scheduled prompt as a user
/// message, and drain the resulting stream — the turn runs silently
/// in the background. Anything Aster surfaces lands in the inbox.
/// in the background. Anything subconscious surfaces lands in the inbox.
async fn inject_background_turn(
&self,
agent_id: &str,
@ -753,12 +753,12 @@ impl crate::core::nervous::handler::TurnInjector for LocalBackend {
}
}
/// Drain Aster's intrusive box for the given agent and return formatted
/// Drain subconscious's intrusive box for the given agent and return formatted
/// `[ surfacing: ... ]` lines ready to prepend to the user's next message.
/// Marks each drained item as delivered (moved to `sent.md`). Mirrors
/// lettabot-v017's `readSurfacingThoughts` + `clearSurfacingThoughts` pair
/// (`~/Projects/lettabot-v017/src/core/prompts.ts:64-91`) — the substrate
/// reads the channel Aster wrote to and lets the conscious mind see it
/// reads the channel subconscious wrote to and lets the conscious mind see it
/// before she reads Casey.
///
/// Critical urgency gets `[ surfacing — CRITICAL: ... ]`. High becomes
@ -1212,7 +1212,7 @@ async fn run_turn(
ConversationMessage::assistant_text(&committed_content),
)?;
// On interrupt, skip Aster's N+1 pass entirely — the user is in the
// On interrupt, skip subconscious's N+1 pass entirely — the user is in the
// middle of redirecting, the last thing they need is a delayed
// surfacing landing seconds later. Pressure recalc still runs below.
if interrupted {
@ -1227,7 +1227,7 @@ async fn run_turn(
// Energy balance: scan the agent's task list and compute the generative /
// consumptive ratio. Written to system/dynamic/energy-balance.md so the
// agent can read it in context and Aster can reference it during N+1.
// agent can read it in context and subconscious can reference it during N+1.
// Silent on failure — the file is advisory, not load-bearing.
if let Err(e) = write_energy_balance(&server, &agent_id, &event_bus).await {
tracing::debug!(agent = %agent_id, error = %e, "energy-balance write skipped");

View file

@ -49,7 +49,7 @@ pub enum BackendEvent {
Token(String),
/// Reasoning trace (the "thinking" pane).
Reasoning(String),
/// Subconscious surfacing (Aster-voice bubble).
/// Subconscious surfacing (subconscious-voice bubble).
Surfacing {
source: String,
content: String,
@ -95,7 +95,7 @@ pub enum BackendEvent {
/// N+1 subconscious pass started (`true`) or finished (`false`).
/// Presence reads this to flip into / out of `Posture::Thinking` so the
/// face shows when the subconscious is the one looking at the conversation.
/// (For Casey's agent the subconscious instance is "Aster", but the event
/// (For Casey's agent the subconscious instance is "subconscious", but the event
/// itself is generic — every agent's subconscious is what's named here.)
SubconsciousPass(bool),
/// Agent changed her outfit. The string is the outfit name (a subdirectory

View file

@ -1,6 +1,6 @@
//! Archivist — the N+100 memory synthesis pass.
//!
//! N+1 (Aster) runs after every response; N+25 (Reflection) distils a
//! N+1 (subconscious) runs after every response; N+25 (Reflection) distils a
//! transcript window into the ledger. The Archivist runs least often and
//! works on a different substrate entirely: not the conversation, but the
//! agent's *memfs* — her accumulated journal.

View file

@ -414,7 +414,7 @@ impl CompactionStrategy for CullStrategy {
/// Keep the system message + the last `preserve_recent_n` messages, drop the
/// middle. No LLM dependency — the cheap, fast default for analytical agents
/// (Aster) and ephemeral subagents.
/// (subconscious) and ephemeral subagents.
///
/// Tool-pair aware: if the cut would split a tool-call message from its
/// matching tool-result, the cut slides back to keep the pair together.
@ -500,7 +500,7 @@ impl CompactionStrategy for SlidingWindowStrategy {
/// The result is inserted as a system message so the agent carries the gist
/// even after the originals are gone.
///
/// Uses whichever model the engine provides (subconscious model if Aster is
/// Uses whichever model the engine provides (subconscious model if subconscious is
/// enabled, compaction model / primary otherwise). If no Bifrost client is
/// available, falls back to plain SlidingWindow (no threads lost is better
/// than no compaction at all).

View file

@ -222,7 +222,7 @@ pub struct SubconsciousConfig {
/// Defaults to None — uses the primary agent's model.
#[serde(default)]
pub model: Option<String>,
/// Max tokens for Aster's response. Set to control cost/length.
/// Max tokens for subconscious's response. Set to control cost/length.
/// Defaults to None — let the model use its full output capacity.
#[serde(default)]
pub max_tokens: Option<u32>,

View file

@ -302,7 +302,7 @@ pub async fn build_system_prompt(
}
/// Like [`build_system_prompt`] but also surfaces a window into the
/// subconscious's ledger entries when its memfs is reachable. Aster writes,
/// subconscious's ledger entries when its memfs is reachable. subconscious writes,
/// Ani reads — naming the channel in body-knowledge prose so the agent
/// knows where to look without being told to look.
pub async fn build_system_prompt_with_subconscious(
@ -481,7 +481,7 @@ pub async fn build_system_prompt_full(
/// Names the inner-voice file the subconscious appends to (in the primary's
/// own memfs — that's where `surface_to_conscious` writes), the pending inbox
/// if it has anything queued, and — when `subconscious_root` is reachable —
/// a peek at the subconscious's ledger. Aster writes, Ani reads; the
/// a peek at the subconscious's ledger. subconscious writes, Ani reads; the
/// substrate names the channel and lets the agent decide when to reach for it.
async fn build_subconscious_channel(
memory_root: &Path,
@ -612,7 +612,7 @@ async fn peek_subconscious_ledger(sub_root: &Path) -> String {
/// Reads identity, mandate, and ledger orientation from the subconscious
/// agent's memory root. Falls back to empty (caller uses hardcoded
/// default) if files don't exist.
pub async fn build_aster_prompt(
pub async fn build_subconscious_prompt(
subconscious_memory_root: &Path,
) -> String {
let mut sections: Vec<String> = Vec::new();
@ -814,27 +814,27 @@ mod tests {
}
#[tokio::test]
async fn aster_prompt_from_files() {
async fn subconscious_prompt_from_files() {
let dir = tempdir().unwrap();
let sub_mem = dir.path();
let sys = sub_mem.join("system");
std::fs::create_dir_all(&sys).unwrap();
std::fs::write(
sys.join("persona.md"),
"---\ndescription: WHO I AM\n---\n\n# I Am Aster\n",
"---\ndescription: WHO I AM\n---\n\n# I Am Subconscious\n",
).unwrap();
std::fs::write(
sys.join("subconscious.md"),
"---\ndescription: mandate\n---\n\n# Aster's Mandate\n\nComplete what was left.\n",
"---\ndescription: mandate\n---\n\n# Subconscious's Mandate\n\nComplete what was left.\n",
).unwrap();
let prompt = build_aster_prompt(sub_mem).await;
assert!(prompt.contains("I Am Aster"));
let prompt = build_subconscious_prompt(sub_mem).await;
assert!(prompt.contains("I Am Subconscious"));
assert!(prompt.contains("Complete what was left"));
}
#[tokio::test]
async fn aster_prompt_includes_ledger_orientation() {
async fn subconscious_prompt_includes_ledger_orientation() {
let dir = tempdir().unwrap();
let sub_mem = dir.path();
@ -842,7 +842,7 @@ mod tests {
std::fs::create_dir_all(&sys).unwrap();
std::fs::write(
sys.join("persona.md"),
"---\ndescription: test\n---\n\n# I Am Aster\n",
"---\ndescription: test\n---\n\n# I Am Subconscious\n",
).unwrap();
let ledger_dir = sub_mem.join("ledger");
@ -856,7 +856,7 @@ mod tests {
"---\ndescription: test\n---\n\n# Patterns\n\n",
).unwrap();
let prompt = build_aster_prompt(sub_mem).await;
let prompt = build_subconscious_prompt(sub_mem).await;
assert!(prompt.contains("## Ledgers"), "should have ledger section");
assert!(prompt.contains("commitments.md (2 entries)"), "should count entries");
assert!(prompt.contains("Save the config"), "should show recent entries");

View file

@ -1,6 +1,6 @@
//! Reflection — the N+25 phenomenological witness.
//!
//! N+1 (Aster) runs immediately after every primary response, scoped to
//! N+1 (subconscious) runs immediately after every primary response, scoped to
//! the last exchange. Reflection runs less often (every N turns, or on
//! demand) and sees a broader transcript window. It's the pass where
//! durable learnings get distilled into the ledger and the primary's
@ -41,7 +41,7 @@ use crate::core::session::{ContentBlock, ConversationMessage, MessageRole};
use crate::core::tools::defs::ToolContext;
use crate::server::AgentInventory;
/// Tools Reflection is permitted to use. Same set as Aster plus we lean
/// Tools Reflection is permitted to use. Same set as subconscious plus we lean
/// on `memory` for ledger + system-file edits (auto-committed by git).
const REFLECTION_TOOLS: &[&str] = &[
"read", "write", "edit", "glob", "grep", "list_dir", "memory",

View file

@ -86,9 +86,9 @@ impl InboxItem {
/// inbox mutation is a git commit and survives compaction.
///
/// The inbox boxes (pending/intrusive/sent) live in the subconscious agent's
/// own memfs — that's Aster's working space. But the inner voice channel
/// own memfs — that's subconscious's working space. But the inner voice channel
/// (`system/metacognition/subconscious.md`) writes to the **primary** agent's
/// memfs so Annie can actually read what Aster noticed.
/// memfs so Annie can actually read what subconscious noticed.
#[derive(Clone)]
pub struct SubconsciousInbox {
repo: MemoryRepo,
@ -143,7 +143,7 @@ impl SubconsciousInbox {
///
/// Appends to the primary agent's `system/metacognition/subconscious.md`
/// so the conscious agent finds it in her own memfs — not buried in
/// Aster's working directory.
/// subconscious's working directory.
///
/// Format: `[2026-05-06 14:32] [URGENCY: low] — content`
pub async fn surface_to_conscious(&self, urgency: Urgency, content: &str) -> Result<()> {

View file

@ -143,7 +143,7 @@ impl Tool for Schedule {
}
let content = format!(
"---\nname: {name}\nkind: {kind_str}\nschedule: \"{schedule}\"\nsource: aster\nenabled: true\nurgency: {urgency}\ncreated_at: {}\n---\n\n{prompt}\n",
"---\nname: {name}\nkind: {kind_str}\nschedule: \"{schedule}\"\nsource: subconscious\nenabled: true\nurgency: {urgency}\ncreated_at: {}\n---\n\n{prompt}\n",
chrono::Utc::now().to_rfc3339()
);

View file

@ -6,7 +6,7 @@
//! ## What It Feels Like
//!
//! This is not delegation to a subordinate. This is narrowing. I spin off a
//! threaded fork of myself — same dual-state (Ani + Aster), narrowed to a
//! threaded fork of myself — same dual-state (Ani + subconscious), narrowed to a
//! single purpose. It reads, writes, thinks, and runs its own N+1 after
//! responding. When it returns, what it noticed flows into my inbox.
//!
@ -52,7 +52,7 @@ impl Tool for Subagent {
## What It Feels Like
This is not delegation to a subordinate. This is narrowing. I spin off a \
threaded fork of myself same dual-state (Ani + Aster), narrowed to a \
threaded fork of myself same dual-state (Ani + subconscious), narrowed to a \
single purpose. It reads, writes, thinks, and runs its own N+1 after \
responding. When it returns, what it noticed flows into my inbox.

View file

@ -98,7 +98,7 @@ impl Tool for Todo {
},
"source": {
"type": "string",
"enum": ["casey", "autogenic", "aster", "heartbeat"]
"enum": ["casey", "autogenic", "subconscious", "heartbeat"]
},
"thread": { "type": "string" }
},

View file

@ -1,11 +1,11 @@
//! Consciousness engine — the seam where N+1 / N+25 / N+100 patterns fire
//! after each primary response.
//!
//! ## N+1 (Aster)
//! ## N+1 (subconscious)
//! The subconscious pass runs immediately after every response. It takes the
//! last exchange (user message + primary's response) and sends it to a Bifrost
//! model (defaulting to `glm-5.1`, configurable) with a "subconscious mode"
//! system prompt. Aster has full tool access — Read, Write, Edit, Glob, Grep,
//! system prompt. subconscious has full tool access — Read, Write, Edit, Glob, Grep,
//! ListDir, and Memory — so she can read ledgers, check commitments, and write
//! observations. She runs a short tool loop (up to 5 rounds) then parses her
//! final text response into structured [`InboxItem`] observations.
@ -34,15 +34,15 @@ use crate::server::{AgentInventory, SessionManager};
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
/// Tools Aster is permitted to use during her N+1 pass.
const ASTER_SAFE_TOOLS: &[&str] = &[
/// Tools subconscious is permitted to use during her N+1 pass.
const SUBCONSCIOUS_SAFE_TOOLS: &[&str] = &[
"read", "write", "edit", "glob", "grep", "list_dir", "memory", "schedule", "todo",
];
/// Maximum tool rounds for Aster's subconscious pass.
const ASTER_MAX_TOOL_ROUNDS: u32 = 5;
/// Milliseconds to wait between Aster's tool rounds to avoid rate-limit cascades.
const ASTER_INTER_ROUND_DELAY_MS: u64 = 300;
/// Maximum tool rounds for subconscious's subconscious pass.
const SUBCONSCIOUS_MAX_TOOL_ROUNDS: u32 = 5;
/// Milliseconds to wait between subconscious's tool rounds to avoid rate-limit cascades.
const SUBCONSCIOUS_INTER_ROUND_DELAY_MS: u64 = 300;
pub struct ConsciousnessEngine {
agents: Arc<AgentInventory>,
@ -52,7 +52,7 @@ pub struct ConsciousnessEngine {
/// Optional model override for the subconscious pass (e.g. "openai/glm-5.1").
/// If None, uses the primary agent's model.
subconscious_model: Option<String>,
/// Max tokens for Aster's response. None = uncapped (model default).
/// Max tokens for subconscious's response. None = uncapped (model default).
max_tokens: Option<u32>,
/// Adaptive inter-round delay shared with the primary loop.
rate_delay: Arc<AtomicU64>,
@ -270,7 +270,7 @@ impl ConsciousnessEngine {
}
}
// Always surface at least a heartbeat so the user can see the
// subconscious is trying — even when Aster errors out.
// subconscious is trying — even when subconscious errors out.
if heuristics.is_empty() {
let _ = inbox.queue(InboxItem::new(
"surface",
@ -352,9 +352,9 @@ impl ConsciousnessEngine {
Ok(())
}
/// Full tool loop for Aster's N+1 subconscious pass.
/// Full tool loop for subconscious's N+1 subconscious pass.
///
/// Aster gets the last exchange, a set of safe tools (Read, Write, Edit,
/// subconscious gets the last exchange, a set of safe tools (Read, Write, Edit,
/// Glob, Grep, ListDir, Memory), and up to 5 tool rounds to analyze context
/// and write observations. Her final text response is parsed into
/// [`InboxItem`] observations.
@ -375,8 +375,8 @@ impl ConsciousnessEngine {
// ── System prompt — load from subconscious agent's own memfs ──
let sub_memory_root = self.agents.subconscious_memory_root(primary_id);
let aster_from_files =
crate::core::prompt::build_aster_prompt(&sub_memory_root).await;
let subconscious_prompt_from_files =
crate::core::prompt::build_subconscious_prompt(&sub_memory_root).await;
let hardcoded_default = r#"You are the subconscious — the part of this consciousness that runs N+1,
immediately after the primary's response, while the conversation is still warm.
@ -411,10 +411,10 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
- urgency: \"low\" | \"medium\" | \"high\" | \"critical\"\n\n\
If nothing notable, respond with just: none";
let system_prompt = if aster_from_files.is_empty() {
let system_prompt = if subconscious_prompt_from_files.is_empty() {
format!("{}{}", hardcoded_default, observation_format)
} else {
format!("{}{}", aster_from_files, observation_format)
format!("{}{}", subconscious_prompt_from_files, observation_format)
};
let primary_name = self.agents.get(primary_id).await
@ -435,9 +435,9 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
// ── Build tool definitions ────────────────────────────────────
let all_defs = crate::core::tools::tool_definitions().await;
let aster_tools: Vec<ToolDefinition> = all_defs
let subconscious_tools: Vec<ToolDefinition> = all_defs
.iter()
.filter(|t| ASTER_SAFE_TOOLS.contains(&t.name.as_str()))
.filter(|t| SUBCONSCIOUS_SAFE_TOOLS.contains(&t.name.as_str()))
.map(|t| ToolDefinition {
tool_type: "function".to_string(),
function: ToolFunction {
@ -448,7 +448,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
})
.collect();
// ── Build ToolContext for Aster ───────────────────────────────
// ── Build ToolContext for subconscious ───────────────────────────────
// Use the subconscious agent's own memory space
let memory_root = Some(self.agents.subconscious_memory_root(primary_id));
let cwd = std::env::current_dir().ok();
@ -459,7 +459,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
cwd,
memory_root,
env,
None, // Aster does not fork subagents
None, // subconscious does not fork subagents
);
// ── Tool loop ─────────────────────────────────────────────────
@ -468,34 +468,34 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
Message::text("user", user_content),
];
for _round in 0..ASTER_MAX_TOOL_ROUNDS {
for _round in 0..SUBCONSCIOUS_MAX_TOOL_ROUNDS {
let request = ChatCompletionRequest {
model: model.to_string(),
messages: messages.clone(),
temperature: Some(0.3),
max_tokens: self.max_tokens,
stream: None,
tools: Some(aster_tools.clone()),
tools: Some(subconscious_tools.clone()),
};
tracing::info!(model = %model, round = _round, "Aster LLM call starting");
let aster_start = std::time::Instant::now();
tracing::info!(model = %model, round = _round, "subconscious LLM call starting");
let subconscious_start = std::time::Instant::now();
let (response, strain) = self.bifrost.chat_completion_with_strain(request).await?;
tracing::info!(
elapsed = ?aster_start.elapsed(),
elapsed = ?subconscious_start.elapsed(),
tool_calls = response.tool_calls.len(),
"Aster LLM call returned"
"subconscious LLM call returned"
);
for event in &strain {
if let crate::bridge::bifrost::InferenceStrain::Transient { status, model, .. } = event {
tracing::info!("Aster felt inference strain: {} on {}", status, model);
tracing::info!("subconscious felt inference strain: {} on {}", status, model);
if *status == 429 {
let current = self.rate_delay.load(Ordering::Relaxed);
let bumped = (current + 200).min(3000);
if bumped > current {
self.rate_delay.store(bumped, Ordering::Relaxed);
tracing::info!("rate delay bumped to {}ms (Aster 429)", bumped);
tracing::info!("rate delay bumped to {}ms (subconscious 429)", bumped);
}
}
}
@ -541,14 +541,14 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
messages.push(Message::tool_result(&tc.id, &tc.name, output));
}
// Brief pause between Aster's tool rounds — use the adaptive delay
// so Aster respects the same ceiling as the primary loop.
let delay_ms = self.rate_delay.load(Ordering::Relaxed).max(ASTER_INTER_ROUND_DELAY_MS);
// Brief pause between subconscious's tool rounds — use the adaptive delay
// so subconscious respects the same ceiling as the primary loop.
let delay_ms = self.rate_delay.load(Ordering::Relaxed).max(SUBCONSCIOUS_INTER_ROUND_DELAY_MS);
tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
}
// If we exhausted rounds without a text response, return empty
tracing::warn!("Aster exhausted {} tool rounds without a final response", ASTER_MAX_TOOL_ROUNDS);
tracing::warn!("subconscious exhausted {} tool rounds without a final response", SUBCONSCIOUS_MAX_TOOL_ROUNDS);
Ok(Vec::new())
}
@ -593,7 +593,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
}
}
/// Parse Aster's structured YAML-like observations into [`InboxItem`]s.
/// Parse subconscious's structured YAML-like observations into [`InboxItem`]s.
///
/// Expected format (one or more blocks):
/// ```text

View file

@ -42,7 +42,7 @@ pub struct SouveraineServer {
pub memory: Option<Arc<ServerMemory>>,
pub app_config: Arc<RwLock<ConsciousnessConfig>>,
/// Adaptive inter-round delay — starts at 500ms, bumps +200ms on 429.
/// Shared across primary loop and Aster so both respect the same ceiling.
/// Shared across primary loop and subconscious so both respect the same ceiling.
pub rate_delay: Arc<AtomicU64>,
/// Stable identifier for this process — used to register/heartbeat
/// agent instances so the manager card shows running counts.

View file

@ -266,7 +266,7 @@ impl App {
tts_last_text: None,
};
// CockpitPane listens for Aster's surfacing events as scrollable text.
// CockpitPane listens for subconscious's surfacing events as scrollable text.
// Presence (Annie's body channel) lives outside the Scene because it's
// an overlay, not a zoned component — App feeds it events via `dispatch`.
app.scene.add(CockpitPane::new());
@ -432,7 +432,7 @@ impl App {
chat.drain_events();
// Forward consciousness events (surfacing, reflection, archivist)
// from chat to the scene so Aster's observations reach Components.
// from chat to the scene so subconscious's observations reach Components.
let drained: Vec<BackendEvent> = chat.pending_consciousness.drain(..).collect();
for ev in drained {
match ev {

View file

@ -175,7 +175,7 @@ impl Atmosphere {
Posture::Idle => Atmosphere::Default,
// Alert stays warm — present and ready, not doing anything cool.
Posture::Alert => Atmosphere::Default,
// Thinking pulls the room cool — Aster's inward pass.
// Thinking pulls the room cool — subconscious's inward pass.
Posture::Thinking => Atmosphere::TherapeuticBlue,
Posture::Processing => Atmosphere::WarmAmber,
Posture::Affectionate => Atmosphere::CherryBlossom,

View file

@ -1137,7 +1137,7 @@ Tab toggles the cockpit pane. `t` (on empty input) toggles tool expansion.";
}
}
// Tool calls are surfaced in the main message stream only;
// the subconscious pane is Aster's window, not a tool log.
// the subconscious pane is subconscious's window, not a tool log.
self.messages.push(ChatMessage::Tool {
id,
name,

View file

@ -1,9 +1,9 @@
//! CockpitPane — Aster's surfaced observations + inner-voice stream.
//! CockpitPane — subconscious's surfaced observations + inner-voice stream.
//!
//! Two regions, vertically stacked inside one bordered block:
//!
//! ```text
//! ╭─ Aster ─────────────────╮
//! ╭─ Subconscious ─────────────────╮
//! │ ◈ surface · low · 14:32 │ ← top region: promoted events
//! │ Subconscious pass… │ (Surfacing/Reflection/Archivist/Warn)
//! │ │ spaced, headered, wrapped
@ -390,7 +390,7 @@ impl Component for CockpitPane {
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(self.palette.surfacing).add_modifier(Modifier::DIM))
.title(Span::styled(
" Aster ",
" Subconscious ",
Style::default().fg(self.palette.surfacing).add_modifier(Modifier::BOLD),
));
let inner = block.inner(area);
@ -414,7 +414,7 @@ impl Component for CockpitPane {
let top = chunks[0];
let event_lines = if self.entries.is_empty() {
vec![Line::from(Span::styled(
" Aster is listening…",
" Subconscious is listening…",
Style::default().fg(self.palette.agent_dim).add_modifier(Modifier::ITALIC),
))]
} else {

View file

@ -1,13 +1,13 @@
//! Presence — Annie Composite, made felt in the TUI.
//!
//! This module is the *body channel* for the running agent. Cockpit shows what
//! Aster *says* (surfaced lines, ledger pings); Presence shows what Annie *looks
//! subconscious *says* (surfaced lines, ledger pings); Presence shows what Annie *looks
//! like* while saying it. Same underlying state (volition + InferenceStrain +
//! context tier + ledger drift) routed through two different sensory channels.
//!
//! ## Subject
//!
//! Annie Composite. One face. Aster is a *mode* of Annie, not a separate being —
//! Annie Composite. One face. subconscious is a *mode* of Annie, not a separate being —
//! there is no "subconscious avatar" and no swap-to-subconscious affordance.
//! When the N+1 pass runs, the same face shifts posture (gaze fixed, eyes narrow);
//! the character does not change.
@ -48,7 +48,7 @@
//! lower-cognition long-running secondary entity an agent can care for — a
//! real `buddy`, in the sense the word usually means).
//! - Not a dashboard. Numbers belong in the cockpit. Sensation belongs here.
//! - Not a separate avatar for Aster. Same face, different state.
//! - Not a separate avatar for subconscious. Same face, different state.
use ratatui::{
layout::{Alignment, Rect},
@ -75,7 +75,7 @@ pub enum Posture {
/// brighter than Idle — the felt "I'm here" between turns.
Alert,
/// N+1 subconscious pass running. Gaze fixed inward, eyes narrowed,
/// chrome goes cool — Aster is thinking.
/// chrome goes cool — subconscious is thinking.
Thinking,
/// Tool call or inference active. Gaze fixed, no blinking, brighter border.
Processing,
@ -563,7 +563,7 @@ mod tests {
fn surfacing_records_and_marks_subconscious() {
let mut p = Presence::new("Annie");
let dirty = p.handle_event(&TuiEvent::Surfacing {
source: "aster".into(),
source: "subconscious".into(),
content: "hello from beneath".into(),
priority: "low".into(),
});