use anyhow::{Context, Result};
use std::path::Path;
use trusty_common::memory_core::store::PalaceStore;
const DEFAULT_PALACE_ID: &str = "localLLM";
const LEGACY_DEFAULT_NAME: &str = "localLLM";
const NEW_DEFAULT_NAME: &str = "User Memories";
pub fn migrate_default_palace_name(registry_root: &Path) -> Result<()> {
let palace_dir = registry_root.join(DEFAULT_PALACE_ID);
if !palace_dir.join("palace.json").exists() {
return Ok(());
}
let mut palace = PalaceStore::load_palace(&palace_dir)
.with_context(|| format!("load palace metadata at {}", palace_dir.display()))?;
if palace.name != LEGACY_DEFAULT_NAME {
return Ok(());
}
palace.name = NEW_DEFAULT_NAME.to_string();
PalaceStore::save_palace(&palace)
.with_context(|| format!("rewrite palace metadata at {}", palace_dir.display()))?;
tracing::info!(
palace_id = DEFAULT_PALACE_ID,
old_name = LEGACY_DEFAULT_NAME,
new_name = NEW_DEFAULT_NAME,
"migrated default palace display name"
);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Utc;
use tempfile::tempdir;
use trusty_common::memory_core::palace::{Palace, PalaceId};
fn persist_palace(registry_root: &Path, id: &str, name: &str) -> Palace {
let data_dir = registry_root.join(id);
std::fs::create_dir_all(&data_dir).expect("create palace data dir");
let palace = Palace {
id: PalaceId::new(id),
name: name.to_string(),
description: None,
created_at: Utc::now(),
data_dir: data_dir.clone(),
};
PalaceStore::save_palace(&palace).expect("persist palace metadata");
palace
}
#[test]
fn migrates_when_legacy_name() {
let tmp = tempdir().unwrap();
let root = tmp.path();
persist_palace(root, DEFAULT_PALACE_ID, LEGACY_DEFAULT_NAME);
migrate_default_palace_name(root).expect("migration runs");
let loaded =
PalaceStore::load_palace(&root.join(DEFAULT_PALACE_ID)).expect("reload palace.json");
assert_eq!(loaded.id.as_str(), DEFAULT_PALACE_ID, "id must be stable");
assert_eq!(
loaded.name, NEW_DEFAULT_NAME,
"display name must be updated"
);
}
#[test]
fn idempotent_when_already_renamed() {
let tmp = tempdir().unwrap();
let root = tmp.path();
persist_palace(root, DEFAULT_PALACE_ID, LEGACY_DEFAULT_NAME);
migrate_default_palace_name(root).expect("first migration");
migrate_default_palace_name(root).expect("second migration is no-op");
let loaded =
PalaceStore::load_palace(&root.join(DEFAULT_PALACE_ID)).expect("reload palace.json");
assert_eq!(loaded.name, NEW_DEFAULT_NAME);
}
#[test]
fn leaves_custom_name_untouched() {
let tmp = tempdir().unwrap();
let root = tmp.path();
persist_palace(root, DEFAULT_PALACE_ID, "My Custom Memories");
migrate_default_palace_name(root).expect("migration runs");
let loaded =
PalaceStore::load_palace(&root.join(DEFAULT_PALACE_ID)).expect("reload palace.json");
assert_eq!(
loaded.name, "My Custom Memories",
"custom names must not be overwritten"
);
}
#[test]
fn pre_renamed_user_memories_is_untouched() {
let tmp = tempdir().unwrap();
let root = tmp.path();
persist_palace(root, DEFAULT_PALACE_ID, NEW_DEFAULT_NAME);
migrate_default_palace_name(root).expect("migration runs");
let loaded =
PalaceStore::load_palace(&root.join(DEFAULT_PALACE_ID)).expect("reload palace.json");
assert_eq!(loaded.name, NEW_DEFAULT_NAME);
}
#[test]
fn missing_palace_is_noop() {
let tmp = tempdir().unwrap();
migrate_default_palace_name(tmp.path()).expect("missing palace is a no-op");
}
#[test]
fn unrelated_palaces_are_not_touched() {
let tmp = tempdir().unwrap();
let root = tmp.path();
persist_palace(root, "other-palace", LEGACY_DEFAULT_NAME);
migrate_default_palace_name(root).expect("migration runs");
let loaded =
PalaceStore::load_palace(&root.join("other-palace")).expect("reload other palace");
assert_eq!(
loaded.name, LEGACY_DEFAULT_NAME,
"non-default palaces must not be touched"
);
}
}