Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.rstoday, with one change each:fnbecomespub(super) fn, so thatserve.rscan still call them. It has its ownuselines for what they use. - In
serve.rs: the two functions are deleted,mod admin;is added, and the imports only they used go (MemoryAdminReply,MemoryCommandandReindexedfrombxcore, andcrate::honcho::report).serve.rsis then 433 lines. - A child module sees its parent's private items:
admin.rsusesMemoryd's private fields, its private methods (lock,mirror,index_notes), the privateStateand the private functionerror, as the code inserve.rsdid, 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:
logwithout a count shows ten edits;rejectdiscards a held write (routing it toacceptpassed every test); what an accepted write moves tonotes.mdis 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 sourcecrates/memoryd/src/serve/admin.rs(a new directory,crates/memoryd/src/serve/, besideserve.rs) and the new testcrates/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.rsdeclaresmod admin;, and Rust reads it fromserve/admin.rs.serve.rsstays where it is; nomod.rsis needed. - Why
pub(super). A method inadmin.rswithout it is private toadmin;pub(super)letsserve(its parent), and nothing further, call it.handleinserve.rscallsself.reindex()andself.admin(..)as before. - The given test
serve_admin.rs(4 tests), through the admin andbrokerdsockets of amemorydit starts:logwith no count (ten of twelve), with 3 and with 20; a rejected write leavesmemory.mdas it was, an accepted one changes it; an acceptedconsolidatethat moves an entry tonotes.mdis found byrecallat once, taggedidentity; a reindex counts one note. - The check that only a move happened: step 5 compares the moved functions in
admin.rswith the ones deleted fromserve.rs, and prints nothing when they are the same but forpub(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 memorydprints no error and no warning. -
4. The tests. With a shell timeout of 600000 ms, every test of
memorydpasses: 4 inserve_admin, and every other file as before:cargo test -p memoryd -
5. Check. Three commands:
-
wc -l crates/memoryd/src/serve.rsprints 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 --statlists onlycrates/memoryd/src/serve.rs, andgit status --shortshowscrates/memoryd/src/serve/andserve_admin.rsas new.
-
-
6. Run the gate.
cargo fmt --all, thenmake gate, with a shell timeout of 600000 ms. Expected last line:gate: ok. The test count is the passed total ofcargo test --workspaceplus one, for the ignoredend_to_endtest that the gate runs on its own: 1448 after this task, 1444 before it; the 4 new ones are the tests ofserve_admin.rs. To count, add up theN passedof everytest result:line the gate'scargo test --workspaceprinted, and add- Never run the gate again only to count.
-
7. Log and commit. Add your row to
docs/implementer-log.md; its Task cell isM5c2/11-memoryd-serve-admin. Stage withgit add crates/memoryd docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.Cargo.lockdoes not change in this task; thegit addis harmless.
Done when
make gateprintsgate: okwith 1448 tests.git diff HEAD~1 --statlists onlyserve.rs,serve/admin.rs,serve_admin.rsanddocs/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 checkin step 3 prints an error or a warning. Quote it, and stop. Do not changeadmin.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
diffprints anything. - The fix seems to need a change anywhere else: another source,
lib.rs, apubonMemoryd'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*.