Review M2b: accept with one follow-up task; record lessons

All ten tasks pass the checklist, the gate, the audit and the device
checks, including a four-turn conversation with a loopd restart and no
cache loss. Reading and probing found four low defects: the busy guard
is released before the final frame on the main path but not on the
three error paths, its Drop skips a poisoned lock, an unreadable
core.md is treated as missing, and bxctl's interactive loop exits on a
failed turn. Task 11 carries the fixes with two new tests, checked
against a fixed copy of the branch.

The Model column is filled in (all Ornith) and one malformed row is
repaired. Two rules are promoted to AGENTS.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 20:47:03 -07:00
co-authored by Claude Fable 5.1
parent 34951084cc
commit 2be8581a0c
8 changed files with 218 additions and 18 deletions
@@ -232,3 +232,28 @@ fn replay_of_a_prefix_is_a_prefix() {
assert_eq!(whole[..part.len()], part[..], "prefix of {n} records");
}
}
/// A core memory file that exists but cannot be read is an error, not silently absent: the owner
/// would otherwise get a session without the memory they curated, and no sign of it.
#[test]
fn an_unreadable_core_memory_file_is_an_error() {
use std::os::unix::fs::PermissionsExt;
if running_as_root() {
return; // root can read anything; the check is meaningless there
}
let home = Home::new();
let cfg = home.config(Path::new("/tmp/unused.sock"));
home.write("memory/core.md", "secret memory\n");
let core = home.dir.join("memory/core.md");
std::fs::set_permissions(&core, std::fs::Permissions::from_mode(0o000)).unwrap();
let result = Baseline::assemble(&cfg, &Registry::m2b());
std::fs::set_permissions(&core, std::fs::Permissions::from_mode(0o644)).unwrap();
let e = result.expect_err("an unreadable core.md must not be ignored");
assert!(e.to_string().contains("core.md"), "{e}");
}
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)
}