Watch
1
0
Fork
You've already forked souveraine
0

feat(tui): atmosphere restore from system/preferences/visual + archive resolved tasks

Adds Atmosphere::from_name() constructor and wires a readback of
system/preferences/visual.md at TUI conversation startup (alongside the
existing energy-balance read), so the agent's last explicit atmosphere
choice survives restarts.

Archives 5 resolved task docs:
- tui-clean-conversation-switch (switch_pending state machine in place)
- scope-4-n25-reflection (408-line engine, fires at N+25)
- tui-presence-and-interrupt (interjection queue, /btw, raise-hand,
  phase display, self-awareness pulse all built)
- presence-visual-evolution (ChatPalette wired, posture-shift fix,
  cross-session atmosphere restore now landed)
- presence-autonomy (atmosphere tool bidirectional, prefs readback now
  wired; from_posture() coupling remains per proto task scope)

Updates CLAUDE.md active task queue to reflect current state.
This commit is contained in:
Fimeg 2026-05-15 17:40:09 -04:00
commit 2ce299f293
2 changed files with 46 additions and 0 deletions

View file

@ -1431,6 +1431,29 @@ impl App {
self.rgp_portrait = crate::ui::rgp::load_portrait_glb(&assets);
}
// Read the agent's last explicit atmosphere from
// system/preferences/visual.md and restore the chrome. The agent
// writes this when she uses the atmosphere tool; the TUI reads it
// at conversation start so her choice survives restarts.
let pref_path = repo.root().join("system").join("preferences").join("visual.md");
if let Ok(content) = std::fs::read_to_string(&pref_path) {
if let Some(body) = content.strip_prefix("---\n") {
if let Some(end) = body.find("\n---\n") {
for line in body[..end].lines() {
if let Some((key, val)) = line.split_once(':') {
let key = key.trim();
let val = val.trim().trim_matches('"');
if key == "atmosphere" {
if let Some(atm) = crate::ui::atmosphere::Atmosphere::from_name(val) {
self.presence.transition_atmosphere(atm);
}
}
}
}
}
}
}
// Read energy balance from the agent's memfs. The file is written
// by the backend after every turn (write_energy_balance in local.rs).
// Parse the YAML frontmatter for generative/consumptive counts and

View file

@ -145,6 +145,29 @@ impl Atmosphere {
}
}
/// Parse an atmosphere from a preset name (case-insensitive, underscore-tolerant).
/// Returns `None` for unknown names and empty strings.
pub fn from_name(name: &str) -> Option<Self> {
let key = name.to_lowercase().replace(' ', "_");
match key.as_str() {
"" | "default" => Some(Atmosphere::Default),
"mint_tea" => Some(Atmosphere::MintTea),
"therapeutic_blue" => Some(Atmosphere::TherapeuticBlue),
"lavender_calm" => Some(Atmosphere::LavenderCalm),
"warm_amber" => Some(Atmosphere::WarmAmber),
"peach_sunset" => Some(Atmosphere::PeachSunset),
"autumn_browns" => Some(Atmosphere::AutumnBrowns),
"neon_glow" => Some(Atmosphere::NeonGlow),
"aurora_borealis" => Some(Atmosphere::AuroraBorealis),
"cherry_blossom" => Some(Atmosphere::CherryBlossom),
"ocean_depths" => Some(Atmosphere::OceanDepths),
"midnight_galaxy" => Some(Atmosphere::MidnightGalaxy),
"twilight_mist" => Some(Atmosphere::TwilightMist),
"forest_greens" => Some(Atmosphere::ForestGreens),
_ => None,
}
}
/// Map a posture to a default atmosphere (when none is explicitly set).
pub fn from_posture(posture: crate::ui::presence::Posture) -> Self {
use crate::ui::presence::Posture;