loopd: the memory block from memoryd, recall and memory as core tools

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
2026-09-25 16:41:44 -07:00
committed by kyle
parent e167fc871b
commit 15ee4b8e3d
14 changed files with 484 additions and 87 deletions
+31 -25
View File
@@ -4,13 +4,13 @@
mod support;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::time::Duration;
use loopd::baseline::Baseline;
use loopd::broker_port::{not_configured_line, unavailable_line};
use loopd::memory::{MemoryPort, MemorySource};
use loopd::session::{Session, SessionError};
use loopd::tools::Registry;
use proto::SessionId;
@@ -64,31 +64,37 @@ fn only_the_torn_error_points_at_the_damaged_log_entry() {
}
#[test]
fn an_unreadable_core_memory_names_its_entry_and_a_missing_system_prompt_does_not() {
fn memoryd_unreachable_or_refusing_names_the_memory_entry() {
let home = Home::new();
let cfg = home.config(Path::new("/tmp/unused.sock"));
home.write("memory/core.md", "memory\n");
let core = home.dir.join("memory/core.md");
if !running_as_root() {
std::fs::set_permissions(&core, std::fs::Permissions::from_mode(0o000)).unwrap();
let result = Baseline::assemble(&cfg, &Registry::m3a());
std::fs::set_permissions(&core, std::fs::Permissions::from_mode(0o644)).unwrap();
let message = result.expect_err("unreadable core.md").to_string();
assert!(message.contains("core.md"), "{message}");
ends_with_pointer(&message, "see docs/runbook.md#core-memory-unreadable");
}
// The system prompt is a different file with a different remedy: no pointer to this entry.
std::fs::remove_file(home.dir.join("system.md")).unwrap();
let message = Baseline::assemble(&cfg, &Registry::m3a())
.expect_err("no system.md")
.to_string();
assert!(!message.contains("core-memory-unreadable"), "{message}");
}
fn running_as_root() -> bool {
std::fs::read_to_string("/proc/self/status")
.map(|s| s.lines().any(|l| l.starts_with("Uid:\t0\t")))
.unwrap_or(false)
let port = MemoryPort::new(
home.dir.join("run/nobody/memory.sock"),
Duration::from_secs(1),
);
let message = port.block().expect_err("nobody is listening");
ends_with_pointer(&message, "see docs/runbook.md#memory-unavailable");
// A refusal from memoryd itself (a file over its limit) ends with the same entry.
let socket = home.dir.join("run/loop-memory/memory.sock");
std::fs::create_dir_all(socket.parent().unwrap()).unwrap();
let listener = std::os::unix::net::UnixListener::bind(&socket).unwrap();
std::thread::spawn(move || {
let (mut stream, _) = listener.accept().unwrap();
let env = proto::read_frame(&mut stream).unwrap();
let reply = proto::Envelope {
v: 1,
id: env.id,
r#final: true,
msg: proto::Message::Error(proto::WireError {
code: proto::ErrorCode::Internal,
detail: "memory/user.md: 1,376 characters; its limit is 1,375".to_string(),
}),
};
proto::write_frame(&mut stream, &reply).unwrap();
});
let message = MemoryPort::new(socket, Duration::from_secs(5))
.block()
.expect_err("memoryd refused");
assert!(message.contains("user.md: 1,376 characters"), "{message}");
ends_with_pointer(&message, "see docs/runbook.md#memory-unavailable");
}
#[test]