Files
boxmaker/docs/plans/M5c2/11-memoryd-serve-admin.md
2026-09-28 21:07:18 -07:00

11 KiB

M5c-2 task 11: serve.rs makes room: the admin socket's requests in a module of their own

Branch: m5c2. Check it: git branch --show-current prints m5c2 and git status --short is empty; otherwise stop. Never switch branch. Commit subject: memoryd: the admin socket's requests move to serve/admin.rs Log row: the Task cell is M5c2/11-memoryd-serve-admin.

Goal

Task 12 adds ask_memory to memoryd's serving, in crates/memoryd/src/serve.rs, which is at 495 lines; the gate refuses any source file over 500. This task makes room, and changes no behaviour: the two functions that answer the admin socket, reindex and admin, move unchanged into a new module, crates/memoryd/src/serve/admin.rs, a child of serve.

  • The new file is given whole, and holds the two functions exactly as they are in serve.rs today, with one change each: fn becomes pub(super) fn, so that serve.rs can still call them. It has its own use lines for what they use.
  • In serve.rs: the two functions are deleted, mod admin; is added, and the imports only they used go (MemoryAdminReply, MemoryCommand and Reindexed from bxcore, and crate::honcho::report). serve.rs is then 433 lines.
  • A child module sees its parent's private items: admin.rs uses Memoryd's private fields, its private methods (lock, mirror, index_notes), the private State and the private function error, as the code in serve.rs did, without making anything else public.
  • New tests pin the moved code. Breaking the moved code on purpose showed that no test covered four of its behaviours: log without a count shows ten edits; reject discards a held write (routing it to accept passed every test); what an accepted write moves to notes.md is indexed at once; a reindex counts the notes. A new test file, serve_admin.rs, covers them. It passes before and after the move.

Every place that changes

This is the complete list.

Place What happens there
crates/memoryd/src/serve/admin.rs new, given whole: reindex and admin, moved
crates/memoryd/src/serve.rs the two functions deleted; mod admin;; two use lines (edits E1 to E4)
crates/memoryd/tests/serve_admin.rs new, given
everything else no change: no other source, no test, no Cargo.toml

Files

  • Copy, from docs/plans/M5c2/files/ (the same path under it): the new source crates/memoryd/src/serve/admin.rs (a new directory, crates/memoryd/src/serve/, beside serve.rs) and the new test crates/memoryd/tests/serve_admin.rs.
  • Modify: crates/memoryd/src/serve.rs, docs/implementer-log.md.
  • Do not touch: any other file. Do not change admin.rs.

Facts you need

  • A module file with a directory of the same name. serve.rs declares mod admin;, and Rust reads it from serve/admin.rs. serve.rs stays where it is; no mod.rs is needed.
  • Why pub(super). A method in admin.rs without it is private to admin; pub(super) lets serve (its parent), and nothing further, call it. handle in serve.rs calls self.reindex() and self.admin(..) as before.
  • The given test serve_admin.rs (4 tests), through the admin and brokerd sockets of a memoryd it starts: log with no count (ten of twelve), with 3 and with 20; a rejected write leaves memory.md as it was, an accepted one changes it; an accepted consolidate that moves an entry to notes.md is found by recall at once, tagged identity; a reindex counts one note.
  • The check that only a move happened: step 5 compares the moved functions in admin.rs with the ones deleted from serve.rs, and prints nothing when they are the same but for pub(super).

The edits, exactly

Nothing else in this file changes. Each quoted "before" block occurs exactly once in the file when its turn comes (make them in order).

crates/memoryd/src/serve.rs

E1. The lines

use bxcore::{
    DataClass, Empty, Envelope, ErrorCode, MemoryAdminReply, MemoryArgs, MemoryBlockReply,
    MemoryCall, MemoryCommand, Message, PROTOCOL_VERSION, RecallArgs, Reindexed, SessionId,
    SessionUpdated, Timestamp, ToolResponse, WireError, read_frame, write_frame,
};

become

use bxcore::{
    DataClass, Empty, Envelope, ErrorCode, MemoryArgs, MemoryBlockReply, MemoryCall, Message,
    PROTOCOL_VERSION, RecallArgs, SessionId, SessionUpdated, Timestamp, ToolResponse, WireError,
    read_frame, write_frame,
};

E2. Remove one line. The lines

use crate::honcho::mirror::Mirror;
use crate::honcho::report;
use crate::honcho::search;

become

use crate::honcho::mirror::Mirror;
use crate::honcho::search;

E3. Add two lines. The line

use crate::{journal, recall, review};

becomes

use crate::{journal, recall, review};

mod admin;

E4. Delete reindex and admin, from the end of index_notes to the end of the file: the lines

        if let Err(e) = state.index.index_notes(&text) {
            (self.log)(format!("memoryd: notes not indexed: {e}").as_str());
        }
    }

    fn reindex(&self) -> Message {
        let mut state = self.lock();
        let max_class = self.config.index.max_class;
        match index::rebuild(
            &self.config.index_path(),
            max_class,
            &self.config.sessions_dir(),
            &self.store.path("notes.md"),
        ) {
            Ok((counts, problems)) => {
                for problem in problems {
                    (self.log)(format!("memoryd: not indexed: {problem}").as_str());
                }
                match Index::open(&self.config.index_path(), max_class) {
                    Ok(index) => {
                        state.index = index;
                        Message::Reindexed(Reindexed {
                            logs: counts.logs,
                            records: counts.records,
                            notes: counts.notes,
                        })
                    }
                    Err(e) => error(e),
                }
            }
            Err(e) => error(e),
        }
    }

    fn admin(&self, command: MemoryCommand, arg: &str) -> Message {
        let state = self.lock();
        let now = Timestamp::now();
        let result = match command {
            MemoryCommand::Log => {
                let n = if arg.is_empty() {
                    10
                } else {
                    match arg.parse::<usize>() {
                        Ok(n) => n,
                        Err(_) => return error(format!("log takes a count; {arg:?} is not one")),
                    }
                };
                journal::log(&self.store, n)
            }
            MemoryCommand::Undo => journal::undo(&self.store, arg, now),
            MemoryCommand::Pending => review::list(&self.store),
            MemoryCommand::Accept => review::accept(&self.store, arg, now),
            MemoryCommand::Reject => review::reject(&self.store, arg, now),
            MemoryCommand::Sync => Ok(match &self.mirror {
                Some(mirror) => mirror.report(),
                None => report::NOT_CONFIGURED.to_string(),
            }),
        };
        if result.is_ok() && matches!(command, MemoryCommand::Undo | MemoryCommand::Accept) {
            self.mirror(None);
        }
        self.index_notes(&state);
        match result {
            Ok(text) => Message::MemoryAdminReply(MemoryAdminReply { text }),
            Err(e) => error(e),
        }
    }
}

become

        if let Err(e) = state.index.index_notes(&text) {
            (self.log)(format!("memoryd: notes not indexed: {e}").as_str());
        }
    }
}

Steps

  • 1. Copy the given test, and see it pass on the tree as it is: the move changes no behaviour. From the repository root:

    cp docs/plans/M5c2/files/crates/memoryd/tests/serve_admin.rs crates/memoryd/tests/
    

    Then cargo test -p memoryd --test serve_admin, with a shell timeout of 600000 ms: 4 tests pass.

  • 2. Copy the new module.

    mkdir crates/memoryd/src/serve
    cp docs/plans/M5c2/files/crates/memoryd/src/serve/admin.rs crates/memoryd/src/serve/
    

    (Until edit E3, nothing declares it, and the build does not read it.)

  • 3. The edits. Make edits E1 to E4, in order. cargo check -p memoryd prints no error and no warning.

  • 4. The tests. With a shell timeout of 600000 ms, every test of memoryd passes: 4 in serve_admin, and every other file as before:

    cargo test -p memoryd
    
  • 5. Check. Three commands:

    • wc -l crates/memoryd/src/serve.rs prints 433.

    • This prints nothing, because the moved functions are the deleted ones but for pub(super):

      diff <(git show HEAD:crates/memoryd/src/serve.rs | sed -n '/^    fn reindex/,/^}/p' | sed '$d') \
        <(sed -n '/pub(super) fn reindex/,/^}/p' crates/memoryd/src/serve/admin.rs | sed '$d' \
          | sed 's/pub(super) fn/fn/')
      
    • git diff --stat lists only crates/memoryd/src/serve.rs, and git status --short shows crates/memoryd/src/serve/ and serve_admin.rs as new.

  • 6. Run the gate. cargo fmt --all, then make gate, with a shell timeout of 600000 ms. Expected last line: gate: ok. The test count is the passed total of cargo test --workspace plus one, for the ignored end_to_end test that the gate runs on its own: 1448 after this task, 1444 before it; the 4 new ones are the tests of serve_admin.rs. To count, add up the N passed of every test result: line the gate's cargo test --workspace printed, and add

    1. Never run the gate again only to count.
  • 7. Log and commit. Add your row to docs/implementer-log.md; its Task cell is M5c2/11-memoryd-serve-admin. Stage with git add crates/memoryd docs/implementer-log.md Cargo.lock, then commit as AGENTS.md says. Cargo.lock does not change in this task; the git add is harmless.

Done when

  • make gate prints gate: ok with 1448 tests.
  • git diff HEAD~1 --stat lists only serve.rs, serve/admin.rs, serve_admin.rs and docs/implementer-log.md.

Stop and report if

  • The branch check at the top fails.
  • Step 1's tests do not all pass before the move.
  • A "before" block is not in the file exactly as shown when its turn comes, or is there more than once.
  • cargo check in step 3 prints an error or a warning. Quote it, and stop. Do not change admin.rs.
  • A test fails in step 4. Do not change a test: stop, and give the failing tests' names and their messages.
  • Step 5's diff prints anything.
  • The fix seems to need a change anywhere else: another source, lib.rs, a pub on Memoryd's fields. It does not; stop and say why you think so.
  • A test outside the given file fails in the gate: check once whether it fails without your change (git stash -u, run it, git stash pop), then stop and report both results.
  • The gate passes with a count other than 1448.

What the given tests pin

With the new tests, each of these changes to the moved code makes at least one test fail (8 tried, all caught; the four marked * passed every test before serve_admin.rs): log's default count changed*, or its count ignored; an undo or accept not decided for Honcho; sync always "not configured"; a reindex keeping the old index, or miscounting the notes*; notes.md not indexed after an admin command*; reject doing what accept does*.