use cersei::prelude::*;
use std::path::Path;
use std::time::Instant;
fn main() {
let mut passed = 0u32;
let mut failed = 0u32;
macro_rules! check {
($name:expr, $cond:expr) => {
if $cond {
passed += 1;
println!(" \x1b[32m✓\x1b[0m {}", $name);
} else {
failed += 1;
println!(" \x1b[31m✗\x1b[0m {}", $name);
}
};
}
println!("\n╔════════════════════════════════════════════════════╗");
println!("║ Phase 5 — Memory & Sessions Stress Test ║");
println!("╚════════════════════════════════════════════════════╝\n");
println!(" 1. Memory Directory (memdir)");
println!(" ───────────────────────────");
{
use cersei_memory::memdir::*;
let tmp = tempfile::tempdir().unwrap();
let mem_dir = tmp.path();
std::fs::write(mem_dir.join("user_role.md"),
"---\nname: User Role\ndescription: Developer prefs\ntype: user\n---\n\nI prefer Rust and Vim."
).unwrap();
std::fs::write(mem_dir.join("project_arch.md"),
"---\nname: Architecture\ndescription: System design\ntype: project\n---\n\nMicroservices with gRPC."
).unwrap();
std::fs::write(
mem_dir.join("feedback_testing.md"),
"---\ntype: feedback\n---\n\nAlways run tests before committing.",
)
.unwrap();
std::fs::write(
mem_dir.join("no_frontmatter.md"),
"Just plain content, no YAML.",
)
.unwrap();
std::fs::write(
mem_dir.join("MEMORY.md"),
"- [User Role](user_role.md) — developer preferences\n\
- [Architecture](project_arch.md) — system design\n\
- [Testing](feedback_testing.md) — testing practices",
)
.unwrap();
let metas = scan_memory_dir(mem_dir);
check!("Scan finds 4 files (excludes MEMORY.md)", metas.len() == 4);
check!(
"No MEMORY.md in results",
metas.iter().all(|m| m.filename != "MEMORY.md")
);
let user = metas.iter().find(|m| m.filename == "user_role.md");
check!("user_role.md found", user.is_some());
check!(
"user_role has name from FM",
user.unwrap().name.as_deref() == Some("User Role")
);
check!(
"user_role has description",
user.unwrap().description.as_deref() == Some("Developer prefs")
);
check!(
"user_role type = User",
user.unwrap().memory_type == Some(MemoryType::User)
);
let plain = metas.iter().find(|m| m.filename == "no_frontmatter.md");
check!(
"Plain file has no FM metadata",
plain.unwrap().name.is_none()
);
let index = load_memory_index(mem_dir);
check!("MEMORY.md loads", index.is_some());
let index = index.unwrap();
check!("Index not truncated (small)", !index.truncated);
check!("Index has content", index.content.contains("User Role"));
let big_dir = tmp.path().join("big");
std::fs::create_dir_all(&big_dir).unwrap();
std::fs::write(big_dir.join("MEMORY.md"), "- line\n".repeat(300)).unwrap();
let big_index = load_memory_index(&big_dir).unwrap();
check!("Large MEMORY.md truncated", big_index.truncated);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
check!("Today = no warning", memory_freshness_text(now).is_none());
check!(
"3 days ago = warning",
memory_freshness_text(now - 86400 * 3).is_some()
);
check!("Age text: today", memory_age_text(now) == "today");
check!(
"Age text: yesterday",
memory_age_text(now - 86400) == "yesterday"
);
check!(
"Sanitize /Users/foo",
sanitize_path_component("/Users/foo") == "_Users_foo"
);
check!(
"Sanitize safe string",
sanitize_path_component("my-project_v2") == "my-project_v2"
);
let prompt = build_memory_prompt_content(mem_dir);
check!("Prompt has content", prompt.contains("User Role"));
let file = load_memory_file(&mem_dir.join("user_role.md"));
check!("Load full file", file.is_some());
let file = file.unwrap();
check!("Content has body", file.content.contains("Rust and Vim"));
check!(
"Content strips FM",
!file.content.contains("name: User Role")
);
println!();
}
println!(" 2. CLAUDE.md Hierarchical Loading");
println!(" ─────────────────────────────────");
{
use cersei_memory::claudemd::*;
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
std::fs::write(
root.join("CLAUDE.md"),
"---\nscope: project\n---\n\n# Project Rules\nUse Rust. Write tests.",
)
.unwrap();
let claude_dir = root.join(".claude");
std::fs::create_dir_all(&claude_dir).unwrap();
std::fs::write(
claude_dir.join("CLAUDE.md"),
"# Local Override\nDebug mode enabled.",
)
.unwrap();
std::fs::write(root.join("extra_rules.md"), "INCLUDED: Always use clippy.").unwrap();
std::fs::write(
root.join("CLAUDE.md"),
"# Project Rules\nUse Rust.\n@include extra_rules.md\nEnd of rules.",
)
.unwrap();
let files = load_all_memory_files(root);
let project = files.iter().find(|f| f.scope == MemoryScope::Project);
let local = files.iter().find(|f| f.scope == MemoryScope::Local);
check!("Project CLAUDE.md loaded", project.is_some());
check!("Local override loaded", local.is_some());
check!("Project before Local", {
let pi = files.iter().position(|f| f.scope == MemoryScope::Project);
let li = files.iter().position(|f| f.scope == MemoryScope::Local);
pi.unwrap_or(99) < li.unwrap_or(0)
});
let project_content = &project.unwrap().content;
check!(
"@include expanded",
project_content.contains("INCLUDED: Always use clippy")
);
check!(
"Content before include present",
project_content.contains("Use Rust")
);
check!(
"Content after include present",
project_content.contains("End of rules")
);
let circ_dir = tmp.path().join("circular");
std::fs::create_dir_all(&circ_dir).unwrap();
std::fs::write(circ_dir.join("a.md"), "@include b.md").unwrap();
std::fs::write(circ_dir.join("b.md"), "@include a.md").unwrap();
std::fs::write(circ_dir.join("CLAUDE.md"), "@include a.md").unwrap();
let circ_files = load_all_memory_files(&circ_dir);
if let Some(f) = circ_files.iter().find(|f| f.scope == MemoryScope::Project) {
check!("Circular @include handled", f.content.contains("circular"));
}
check!(
"Frontmatter stripped",
!local.unwrap().content.contains("scope:")
);
let prompt = build_memory_prompt(&files);
check!("Merged prompt has project", prompt.contains("Use Rust"));
check!("Merged prompt has local", prompt.contains("Debug mode"));
println!();
}
println!(" 3. Session Storage (JSONL)");
println!(" ─────────────────────────");
{
use cersei_memory::session_storage::*;
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("session.jsonl");
let u1 = write_user_entry(&path, "s1", Message::user("Hello"), "/tmp").unwrap();
let a1 = write_assistant_entry(
&path,
"s1",
Message::assistant("Hi there!"),
"/tmp",
Some(&u1),
)
.unwrap();
let u2 = write_user_entry(&path, "s1", Message::user("Fix the bug"), "/tmp").unwrap();
let a2 = write_assistant_entry(&path, "s1", Message::assistant("Done!"), "/tmp", Some(&u2))
.unwrap();
let u3 = write_user_entry(&path, "s1", Message::user("Delete this"), "/tmp").unwrap();
let entries = load_transcript(&path).unwrap();
check!("5 entries written", entries.len() == 5);
tombstone_entry(&path, &u3).unwrap();
let entries = load_transcript(&path).unwrap();
check!("Tombstone removes 1 entry", entries.len() == 4);
let messages = messages_from_transcript(&entries);
check!("4 messages extracted", messages.len() == 4);
check!(
"First message correct",
messages[0].get_text().unwrap() == "Hello"
);
check!(
"Last message correct",
messages[3].get_text().unwrap() == "Done!"
);
check!(
"Tombstoned message gone",
messages
.iter()
.all(|m| m.get_text().unwrap() != "Delete this")
);
tombstone_entry(&path, &a2).unwrap();
let entries = load_transcript(&path).unwrap();
check!("Multiple tombstones work", entries.len() == 3);
let messages = messages_from_transcript(&entries);
for msg in &messages {
check!(
&format!("Message has text: '{}'", msg.get_text().unwrap()),
msg.get_text().is_some()
);
}
let summary = TranscriptEntry::Summary(SummaryEntry {
uuid: "sum-1".into(),
timestamp: chrono::Utc::now().to_rfc3339(),
session_id: "s1".into(),
summary: "User fixed a bug.".into(),
messages_compacted: 3,
});
write_transcript_entry(&path, &summary).unwrap();
let entries = load_transcript(&path).unwrap();
check!("Summary entry persists", entries.len() == 4); println!();
}
println!(" 4. Session Memory Extraction");
println!(" ────────────────────────────");
{
use cersei_agent::session_memory::*;
let small: Vec<Message> = (0..10)
.map(|i| Message::user(format!("Msg {}", i)))
.collect();
let state = SessionMemoryState::default();
check!("10 msgs = no extract", !should_extract(&small, &state));
let large: Vec<Message> = (0..30)
.map(|i| {
if i % 2 == 0 {
Message::user(format!("Q{}", i))
} else {
Message::assistant(format!("A{}", i))
}
})
.collect();
check!("30 msgs = extract", should_extract(&large, &state));
let cooldown_state = SessionMemoryState {
extraction_count: 1,
tool_calls_since_last: 1,
..Default::default()
};
check!(
"Cooldown blocks extract",
!should_extract(&large, &cooldown_state)
);
let output = "\
MEMORY: preference | 8 | User prefers dark mode UI
MEMORY: project | 9 | API uses REST with JSON
MEMORY: decision | 7 | Chose PostgreSQL for storage
MEMORY: pattern | 6 | Uses builder pattern for config
not a memory line
MEMORY: bad format
";
let memories = parse_extraction_output(output);
check!(
&format!("Parsed {} memories", memories.len()),
memories.len() == 4
);
check!(
"First memory content",
memories[0].content == "User prefers dark mode UI"
);
check!(
"Confidence normalized",
(memories[0].confidence - 0.8).abs() < 0.01
);
let tmp = tempfile::tempdir().unwrap();
let target = tmp.path().join("extracted.md");
persist_memories(&memories, &target).unwrap();
let content = std::fs::read_to_string(&target).unwrap();
check!(
"Persisted has section header",
content.contains("Auto-extracted memories")
);
check!("Persisted has memories", content.contains("dark mode"));
check!("Persisted has confidence", content.contains("80%"));
let more = vec![ExtractedMemory {
content: "Uses tokio for async".into(),
category: MemoryCategory::CodePattern,
confidence: 0.9,
}];
persist_memories(&more, &target).unwrap();
let content = std::fs::read_to_string(&target).unwrap();
check!("Append preserves original", content.contains("dark mode"));
check!("Append adds new", content.contains("tokio"));
println!();
}
println!(" 5. Auto-Dream Consolidation");
println!(" ───────────────────────────");
{
use cersei_agent::auto_dream::*;
let tmp = tempfile::tempdir().unwrap();
let mem_dir = tmp.path().join("memory");
let conv_dir = tmp.path().join("conversations");
std::fs::create_dir_all(&mem_dir).unwrap();
std::fs::create_dir_all(&conv_dir).unwrap();
let dream = AutoDream::new(mem_dir.clone(), conv_dir.clone());
let empty_state = ConsolidationState::default();
check!(
"Never consolidated → time gate passes",
dream.time_gate_passes(&empty_state)
);
let recent_state = ConsolidationState {
last_consolidated_at: Some(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
- 3600,
),
..Default::default()
};
check!(
"1 hour ago → time gate fails",
!dream.time_gate_passes(&recent_state)
);
check!(
"No sessions → session gate fails",
!dream.session_gate_passes(&empty_state)
);
for i in 0..6 {
std::fs::write(conv_dir.join(format!("s{}.jsonl", i)), "{}").unwrap();
}
check!(
"6 sessions → session gate passes",
dream.session_gate_passes(&empty_state)
);
check!("No lock → lock gate passes", dream.lock_gate_passes());
dream.acquire_lock().unwrap();
check!("Fresh lock → lock gate fails", !dream.lock_gate_passes());
dream.release_lock().unwrap();
check!("Released lock → lock gate passes", dream.lock_gate_passes());
dream.update_state().unwrap();
let state = dream.load_state();
check!("State persisted", state.last_consolidated_at.is_some());
let prompt = dream.consolidation_prompt();
check!(
"Prompt has phases",
prompt.contains("Orient") && prompt.contains("Prune")
);
println!();
}
println!(" 6. Graph Memory (feature-gated)");
println!(" ──────────────────────────────");
{
use cersei_memory::graph;
let available = graph::is_graph_available();
if available {
println!(" Graph feature ENABLED — running graph tests");
} else {
println!(" Graph feature disabled (expected without --features graph)");
check!("is_graph_available() = false", !available);
let result = graph::GraphMemory::open_in_memory();
check!("open_in_memory fails without feature", result.is_err());
}
println!();
}
println!(" 7. Unified Memory Manager");
println!(" ─────────────────────────");
{
use cersei_memory::manager::MemoryManager;
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
std::fs::write(root.join("CLAUDE.md"), "# Rules\nUse Rust only.").unwrap();
let mem_dir = root.join("memory");
std::fs::create_dir_all(&mem_dir).unwrap();
std::fs::write(
mem_dir.join("MEMORY.md"),
"- [tips](rust_tips.md) — Rust tips",
)
.unwrap();
std::fs::write(
mem_dir.join("rust_tips.md"),
"---\nname: Rust Tips\n---\n\nUse clippy. Use cargo fmt.",
)
.unwrap();
std::fs::write(
mem_dir.join("python_tips.md"),
"---\nname: Python Tips\n---\n\nUse ruff. Use black.",
)
.unwrap();
let sessions_dir = root.join("sessions");
let manager = MemoryManager::new(root)
.with_memory_dir(mem_dir)
.with_sessions_dir(sessions_dir);
let context = manager.build_context();
check!("Context has CLAUDE.md", context.contains("Use Rust only"));
check!("Context has MEMORY.md", context.contains("Rust tips"));
let metas = manager.scan();
check!(
&format!("Scan finds {} memory files", metas.len()),
metas.len() == 2
);
let results = manager.recall("clippy", 10);
check!("Recall finds 'clippy'", results.len() == 1);
check!("Recall content correct", results[0].contains("clippy"));
let results = manager.recall("Use", 10);
check!("Recall multi-match", results.len() == 2);
let results = manager.recall("nonexistent_xyz", 10);
check!("Recall no match", results.is_empty());
manager
.write_user_message("test-sess", Message::user("Hello"))
.unwrap();
manager
.write_assistant_message("test-sess", Message::assistant("Hi"), None)
.unwrap();
manager
.write_user_message("test-sess", Message::user("Bye"))
.unwrap();
let messages = manager.load_session_messages("test-sess").unwrap();
check!("Session has 3 messages", messages.len() == 3);
check!("Session msg 1", messages[0].get_text().unwrap() == "Hello");
check!("Session msg 3", messages[2].get_text().unwrap() == "Bye");
let empty = manager.load_session_messages("nonexistent").unwrap();
check!("Nonexistent session = empty", empty.is_empty());
let sessions = manager.list_sessions();
check!("1 session listed", sessions.len() == 1);
check!("Session ID correct", sessions[0].id == "test-sess");
check!("No graph by default", !manager.has_graph());
let stats = manager.graph_stats();
check!("Graph stats = 0", stats.memory_count == 0);
println!();
}
println!(" 8. Performance Benchmarks");
println!(" ─────────────────────────");
{
use cersei_memory::manager::MemoryManager;
use cersei_memory::memdir::*;
let tmp = tempfile::tempdir().unwrap();
let mem_dir = tmp.path().join("perf_mem");
std::fs::create_dir_all(&mem_dir).unwrap();
for i in 0..100 {
std::fs::write(
mem_dir.join(format!("mem_{:03}.md", i)),
format!("---\nname: Memory {}\ntype: project\n---\n\nContent for memory {} with keywords: rust, testing, deploy, api, database.", i, i)
).unwrap();
}
std::fs::write(
mem_dir.join("MEMORY.md"),
(0..100)
.map(|i| format!("- [mem_{}](mem_{:03}.md) — memory {}", i, i, i))
.collect::<Vec<_>>()
.join("\n"),
)
.unwrap();
let start = Instant::now();
let iters = 50;
for _ in 0..iters {
scan_memory_dir(&mem_dir);
}
let scan_us = start.elapsed().as_micros() / iters as u128;
check!(
&format!("Scan 100 files: {}μs (target < 5000μs)", scan_us),
scan_us < 5000
);
let manager = MemoryManager::new(tmp.path()).with_memory_dir(mem_dir.clone());
let start = Instant::now();
for _ in 0..iters {
manager.recall("testing", 5);
}
let recall_us = start.elapsed().as_micros() / iters as u128;
check!(
&format!("Recall from 100 files: {}μs (target < 10000μs)", recall_us),
recall_us < 10000
);
let start = Instant::now();
for _ in 0..iters {
load_memory_index(&mem_dir);
}
let index_us = start.elapsed().as_micros() / iters as u128;
check!(
&format!("Load MEMORY.md: {}μs (target < 1000μs)", index_us),
index_us < 1000
);
let session_path = tmp.path().join("perf.jsonl");
let start = Instant::now();
for i in 0..100 {
cersei_memory::session_storage::write_user_entry(
&session_path,
"perf",
Message::user(format!("Message {}", i)),
"/tmp",
)
.unwrap();
}
let write_us = start.elapsed().as_micros() / 100;
check!(
&format!("Session write: {}μs/entry (target < 500μs)", write_us),
write_us < 500
);
let start = Instant::now();
for _ in 0..10 {
cersei_memory::session_storage::load_transcript(&session_path).unwrap();
}
let load_us = start.elapsed().as_micros() / 10;
check!(
&format!(
"Session load (100 entries): {}μs (target < 5000μs)",
load_us
),
load_us < 5000
);
println!();
}
println!(" 9. Real User Data Compatibility");
println!(" ───────────────────────────────");
{
let home = dirs::home_dir();
if let Some(home) = home {
let claude_dir = home.join(".claude");
if claude_dir.exists() {
let claude_md = home.join(".claude").join("CLAUDE.md");
if claude_md.exists() {
let files = cersei_memory::claudemd::load_all_memory_files(
&std::env::current_dir().unwrap(),
);
let user_file = files
.iter()
.find(|f| f.scope == cersei_memory::claudemd::MemoryScope::User);
check!("Load real ~/.claude/CLAUDE.md", user_file.is_some());
if let Some(f) = user_file {
println!(" User CLAUDE.md: {} chars", f.content.len());
}
} else {
println!(" No ~/.claude/CLAUDE.md — skipping");
}
let projects_dir = claude_dir.join("projects");
if projects_dir.exists() {
let count = std::fs::read_dir(&projects_dir)
.map(|e| e.count())
.unwrap_or(0);
println!(
" Found {} project directories in ~/.claude/projects/",
count
);
check!("Projects dir accessible", count > 0 || true); }
let sessions_count = std::fs::read_dir(&projects_dir)
.into_iter()
.flatten()
.flatten()
.filter(|e| e.path().is_dir())
.flat_map(|d| std::fs::read_dir(d.path()).into_iter().flatten().flatten())
.filter(|e| e.path().extension().and_then(|x| x.to_str()) == Some("jsonl"))
.count();
println!(" Found {} session transcripts", sessions_count);
} else {
println!(" No ~/.claude/ directory — skipping real data tests");
}
}
println!();
}
println!("╔════════════════════════════════════════════════════╗");
if failed == 0 {
println!(
"║ \x1b[32mALL {} CHECKS PASSED\x1b[0m ║",
passed
);
} else {
println!(
"║ \x1b[31m{} PASSED, {} FAILED\x1b[0m ║",
passed, failed
);
}
println!("╚════════════════════════════════════════════════════╝\n");
if failed > 0 {
std::process::exit(1);
}
}