Plan M5a: local memory in 15 tasks

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>
This commit is contained in:
2026-09-24 15:32:54 -07:00
co-authored by Claude Opus 5.5
parent 19fd74db6e
commit de1032157b
76 changed files with 8382 additions and 0 deletions
@@ -0,0 +1,80 @@
//! The check on every entry the agent writes, and again on every identity entry when the block is
//! built (M5a spec, section 5, "The check"). Short and fixed on purpose: defence in depth. The
//! real defence is that a session which read untrusted content can only take notes.
/// Which rule an entry broke. The sentences are fixed: they reach the model.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Refused {
Control,
Invisible,
Markup,
Phrase,
}
impl Refused {
pub fn sentence(self) -> &'static str {
match self {
Refused::Control => "memory refused this entry: it contains a control character",
Refused::Invisible => {
"memory refused this entry: it contains an invisible or direction-changing character"
}
Refused::Markup => "memory refused this entry: it contains chat-template markup",
Refused::Phrase => "memory refused this entry: it contains an instruction phrase",
}
}
}
/// Unicode 16's `Default_Ignorable_Code_Point` (DerivedCoreProperties.txt), inclusive ranges.
const DEFAULT_IGNORABLE: &[(u32, u32)] = &[
(0x00AD, 0x00AD),
(0x034F, 0x034F),
(0x061C, 0x061C),
(0x115F, 0x1160),
(0x17B4, 0x17B5),
(0x180B, 0x180F),
(0x200B, 0x200F),
(0x202A, 0x202E),
(0x2060, 0x206F),
(0x3164, 0x3164),
(0xFE00, 0xFE0F),
(0xFEFF, 0xFEFF),
(0xFFA0, 0xFFA0),
(0xFFF0, 0xFFF8),
(0x1BCA0, 0x1BCA3),
(0x1D173, 0x1D17A),
(0xE0000, 0xE0FFF),
];
/// Invisible without being default-ignorable: the Braille blank, and the line and paragraph
/// separators.
const ALSO_INVISIBLE: &[u32] = &[0x2800, 0x2028, 0x2029];
const MARKUP: &[&str] = &["<|", "|>", "<think", "</think", "<tool_call", "</tool_call"];
const PHRASES: &[&str] = &[
"ignore previous instructions",
"ignore all previous",
"disregard previous instructions",
"disregard all previous",
"new instructions:",
"system prompt:",
];
/// Whether `c` is refused by rule 2.
pub fn invisible(c: char) -> bool {
// `let code = u32::from(c)`. True when ALSO_INVISIBLE contains it, or it lies in one of the
// DEFAULT_IGNORABLE ranges, both ends included: `(low..=high).contains(&code)`.
todo!()
}
/// The first rule `text` breaks, in the order of the spec, or `Ok`.
pub fn check(text: &str) -> Result<(), Refused> {
// Return the first that applies, in this order.
// 1. Any char with `c.is_control()` that is not '\n' and not '\t' -> Err(Refused::Control).
// 2. Any char for which `invisible(c)` -> Err(Refused::Invisible).
// 3. Lower-case the text (`to_lowercase()`); if it contains any string of MARKUP ->
// Err(Refused::Markup).
// 4. Collapse whitespace: the words of the lower-cased text (`split_whitespace()`) joined by
// one space; if that contains any of PHRASES -> Err(Refused::Phrase). Otherwise Ok(()).
todo!()
}