memoryd (files, the check, edits, journal, index, recall, serving), the memory tools and built-in grants in brokerd, the memory block in loopd, bxctl reindex and memory, and the device checks with memoryd. Each task was built as a reference and gated. Each was replayed on its own at its end state (854 to 936 tests) and mutation-checked. Each skeleton compiles and fails its tests. Every todo comment was read beside its signature. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.2 KiB
M5a task 10: serving
Branch: m5a (run git switch m5a; git status --short must be empty, otherwise stop)
Commit subject: memoryd: serve, three sockets and one lock over the files and the index
Goal
memoryd serve --config <path> (M5a spec, section 7): at start it creates what is missing under
memory/, opens the index and catches it up, then listens on three sockets, one request per
connection, each answered by one frame:
| Socket | Peer | Takes |
|---|---|---|
run/loop-memory/memory.sock |
loopd |
memory_block, session_updated |
run/broker-memory/memory.sock |
brokerd |
memory_call (recall or memory) |
run/owner-memory/admin.sock |
bxctl |
reindex, memory_admin |
A message on the wrong socket is refused (BadMessage). One lock covers the files and the index,
so writes never interleave.
Files
- Copy (skeleton):
crates/memoryd/src/serve.rs - Copy (test):
crates/memoryd/tests/serve.rs - Replace:
crates/memoryd/src/main.rswithdocs/plans/M5a/files/crates/memoryd/src/main.rs-task10(task 03 gave it whole, and nothing has changed it since; this version starts the server) - Modify:
crates/memoryd/src/lib.rs(pub mod serve;afterpub mod review;),crates/memoryd/src/index.rs,docs/implementer-log.md
index.rs, exactly
Add this function just before the doc comment /// Every session log under sessions: (session,
epoch, path), sorted.:
/// The logs of one session: `(epoch, path)`, sorted. A session with no directory has none.
pub fn session_logs(sessions: &Path, session: &str) -> Result<Vec<(i64, PathBuf)>, String> {
let dir = sessions.join(session);
let files = match std::fs::read_dir(&dir) {
Ok(files) => files,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
Err(e) => return Err(format!("{}: {e}", dir.display())),
};
let mut found = Vec::new();
for file in files.filter_map(|f| f.ok()) {
let name = file.file_name().to_string_lossy().into_owned();
if let Some(Ok(epoch)) = name.strip_suffix(".jsonl").map(str::parse::<i64>) {
found.push((epoch, file.path()));
}
}
found.sort();
Ok(found)
}
The skeleton
Written: READ_WAIT, Log, State, Memoryd, Running, Which, error, bad,
Memoryd::lock, and Memoryd::handle, the table of which socket takes which message: read
it first. Fill: start, Running::run, connection, block, session_updated, call,
recall, memory, index_notes, reindex, admin.
Steps
- 1. Copy the two files, replace
main.rs; editlib.rsandindex.rs. - 2. See it fail.
cargo test -p memoryd --test serve: 8 failed. - 3. Fill the functions.
cargo fmt --all. - 4. See it pass. The same command, three times: 8 passed each time, in about a second.
- 5. Run the gate.
make gate:gate: ok, 8 more tests than before (about 920). - 6. Log and commit.
git add -A crates/memoryd docs/implementer-log.md Cargo.lock && git commit
Done when
make gateprintsgate: ok.
Stop and report if
- A test fails after the functions follow their comments exactly. Do not edit a test.
- A test waits more than a few seconds, or passes only sometimes.