memoryd: sync begins when Honcho is turned on, and conclusions are matched by peer
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
//! A turn from before sync began goes nowhere (M5c spec, section 5): the first check, before
|
||||
//! every other, by the time of the turn's `User` record against the ledger's `Began`. Do not
|
||||
//! edit.
|
||||
|
||||
use bxcore::{DataClass, LogRecord, SessionStateReply, Timestamp};
|
||||
use memoryd::honcho::decide::{BEFORE_SYNC, SECRET_CLASS, decide};
|
||||
use memoryd::honcho::ledger::{Action, Workspace};
|
||||
|
||||
const AGENT: &str = "boxmaker0";
|
||||
|
||||
fn at(s: &str) -> Timestamp {
|
||||
Timestamp::parse(s).unwrap()
|
||||
}
|
||||
|
||||
/// One finished turn, its `User` record at `time`.
|
||||
fn turn(time: &str, speaker: Option<&str>) -> Vec<LogRecord> {
|
||||
vec![
|
||||
LogRecord::User {
|
||||
time: at(time),
|
||||
content: "hello".to_string(),
|
||||
speaker: speaker.map(str::to_string),
|
||||
},
|
||||
LogRecord::Assistant {
|
||||
time: at(time),
|
||||
content: Some("hi".to_string()),
|
||||
reasoning_content: None,
|
||||
tool_calls: Vec::new(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn state(class: DataClass) -> SessionStateReply {
|
||||
SessionStateReply {
|
||||
class,
|
||||
untrusted: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn skip(why: &str) -> Action {
|
||||
Action::Skip {
|
||||
why: why.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
const BEGAN: &str = "2026-09-28T10:00:00.000Z";
|
||||
|
||||
#[test]
|
||||
fn the_text() {
|
||||
assert_eq!(BEFORE_SYNC, "the turn is from before sync began");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_turn_from_before_sync_began_goes_nowhere() {
|
||||
let began = Some(at(BEGAN));
|
||||
let clean = state(DataClass::Private);
|
||||
let before = turn("2026-09-28T09:59:59.999Z", Some("kyle"));
|
||||
assert_eq!(
|
||||
decide(&clean, &before, 1, AGENT, began),
|
||||
Ok(skip(BEFORE_SYNC))
|
||||
);
|
||||
let same = turn(BEGAN, Some("kyle"));
|
||||
let to_trusted = Action::Turn {
|
||||
workspace: Workspace::Trusted,
|
||||
speaker: "kyle".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
decide(&clean, &same, 1, AGENT, began),
|
||||
Ok(to_trusted.clone())
|
||||
);
|
||||
let after = turn("2026-09-28T10:00:00.001Z", Some("kyle"));
|
||||
assert_eq!(
|
||||
decide(&clean, &after, 1, AGENT, began),
|
||||
Ok(to_trusted.clone())
|
||||
);
|
||||
// Without a time sync began, no turn is too old.
|
||||
assert_eq!(decide(&clean, &before, 1, AGENT, None), Ok(to_trusted));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_is_the_first_check() {
|
||||
let began = Some(at(BEGAN));
|
||||
// A turn from before that also breaks the class and the speaker rules: before-sync wins.
|
||||
let before = turn("2026-09-28T09:00:00.000Z", None);
|
||||
assert_eq!(
|
||||
decide(&state(DataClass::Secret), &before, 1, AGENT, began),
|
||||
Ok(skip(BEFORE_SYNC))
|
||||
);
|
||||
// The same turn after it: the next rule decides.
|
||||
let after = turn("2026-09-28T11:00:00.000Z", None);
|
||||
assert_eq!(
|
||||
decide(&state(DataClass::Secret), &after, 1, AGENT, began),
|
||||
Ok(skip(SECRET_CLASS))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_turn_by_its_own_time() {
|
||||
let began = Some(at(BEGAN));
|
||||
let mut records = turn("2026-09-28T09:00:00.000Z", Some("kyle"));
|
||||
records.extend(turn("2026-09-28T11:00:00.000Z", Some("kyle")));
|
||||
let clean = state(DataClass::Private);
|
||||
assert_eq!(
|
||||
decide(&clean, &records, 1, AGENT, began),
|
||||
Ok(skip(BEFORE_SYNC))
|
||||
);
|
||||
assert!(matches!(
|
||||
decide(&clean, &records, 2, AGENT, began),
|
||||
Ok(Action::Turn { .. })
|
||||
));
|
||||
}
|
||||
Reference in New Issue
Block a user