# 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 ` (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.rs` with `docs/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;` after `pub 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.`: ```rust /// The logs of one session: `(epoch, path)`, sorted. A session with no directory has none. pub fn session_logs(sessions: &Path, session: &str) -> Result, 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::) { 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`; edit `lib.rs` and `index.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 gate` prints `gate: 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.