From 34db62e3ca4e07a788c8fd686a1b519534025d36 Mon Sep 17 00:00:00 2001 From: Eureka Hands Date: Sat, 7 Nov 2026 00:20:18 +0000 Subject: [PATCH] persona tests: a reply to no brief is told nowhere; the rotation margin ends a start; an idle session tells its own retell, revision or notice; a reply that fails to resolve twice is retried once Co-Authored-By: Claude Sonnet 5.5 --- crates/mind-body/src/persona/tests.rs | 56 ++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/crates/mind-body/src/persona/tests.rs b/crates/mind-body/src/persona/tests.rs index 2262a32..b822f37 100644 --- a/crates/mind-body/src/persona/tests.rs +++ b/crates/mind-body/src/persona/tests.rs @@ -4283,11 +4283,13 @@ struct Timeline<'a> { /// How many of the next keyed in-force reads of a reply fail, and how many of the next resolutions of a reply are refused by the port. keyed_fail: Rc>, admit_fail: Rc>, + /// The resolutions of a reply the port was asked to take, refused ones too. + admits: Rc>, } impl<'a> Timeline<'a> { fn over(mind: &'a TestMind) -> Self { - Self { mind, doing: Rc::default(), reads: Rc::default(), keyed_fail: Rc::default(), admit_fail: Rc::default() } + Self { mind, doing: Rc::default(), reads: Rc::default(), keyed_fail: Rc::default(), admit_fail: Rc::default(), admits: Rc::default() } } } @@ -4317,6 +4319,7 @@ impl MindPort for Timeline<'_> { fn admit(&self, agent: &str, session: &str, documents: Vec) -> Result { let resolves_a_reply = documents.iter().any(|document| matches!(document, PipelineDocument::Resolution(resolution) if resolution.subject.kind == K::Reply)); + self.admits.set(self.admits.get() + u32::from(resolves_a_reply)); if resolves_a_reply && self.admit_fail.get() > 0 { self.admit_fail.set(self.admit_fail.get() - 1); anyhow::bail!("the mind did not take it"); @@ -4406,11 +4409,17 @@ fn resolutions_of(rig: &Rig, subject: &str) -> usize { /// the port refuses, is retried at the next result without telling her the reply again. #[test] fn an_answered_reply_is_told_once_and_resolved_once_when_the_read_or_the_admit_fails() { - for flaw in ["keyed read", "admit"] { + for flaw in ["keyed read", "admit", "both"] { let rig = Rig::new(vec![operator_says(1, "hello")], &[]); let mut act = her_self_asked(); act.push(self_reply("self-r-r1", "The shaders are reviewed.")); - let lines = a_session_with(&rig, vec![act], |timeline| if flaw == "admit" { timeline.admit_fail.set(1) } else { timeline.keyed_fail.set(1) }); + let admits = RefCell::new(Rc::>::default()); + let lines = a_session_with(&rig, vec![act], |timeline| { + timeline.admit_fail.set(u32::from(flaw != "keyed read")); + timeline.keyed_fail.set(u32::from(flaw != "admit")); + *admits.borrow_mut() = timeline.admits.clone(); + }); + assert_eq!(admits.borrow().get(), 2, "{flaw}: tried, then retried once, and never again once the reply is out of force"); assert_eq!(all_replies(&lines).len(), 1, "{flaw}: told once: {lines:?}"); assert!(lines.len() >= 3, "{flaw}: lines followed her answer: {lines:?}"); assert_eq!(outcome_of(&rig, &reply_id("self-r-r1")), heard_outcome(), "{flaw}"); @@ -6514,8 +6523,8 @@ fn the_oldest_waiting_thing_decides_the_next_session() { } #[test] -fn a_reply_to_no_brief_a_public_brief_or_a_brief_with_no_origin_is_told_in_the_public_session() { - assert_eq!(reply_conversation(&[]), Some(Conversation::Public)); +fn a_reply_to_a_public_brief_or_a_brief_with_no_origin_is_told_in_the_public_session_and_a_reply_to_no_brief_in_none() { + assert_eq!(reply_conversation(&[]), None); for origin in [None, Some(MemoryOrigin::Wake), Some(MemoryOrigin::SelfRun { run: "r".into() }), Some(MemoryOrigin::Room { rooms: "swarm".into(), heard_from: vec![] })] { assert_eq!(reply_conversation(&[origin]), Some(Conversation::Public)); } @@ -6530,9 +6539,10 @@ fn a_reply_to_no_brief_a_public_brief_or_a_brief_with_no_origin_is_told_in_the_p for process in processes { let all = process.concat(); let public = all.contains("public asks about oak"); - for words in ["old-brief words", "no-brief words", "public-brief words"] { + for words in ["old-brief words", "public-brief words"] { assert_eq!(all.contains(words), public, "{words} is told in the public session only: {all}"); } + assert!(!all.contains("no-brief words"), "a reply the Self could not attribute is told in no session: {all}"); } } @@ -6647,3 +6657,37 @@ fn an_open_briefs_first_line_entry_is_marked_with_its_origin_as_a_memory_is() { assert!(entry("ask-public").contains("in #swarm, with"), "{}", entry("ask-public")); assert!(!entry("ask-old").contains("privately") && !entry("ask-old").contains(" in #"), "{}", entry("ask-old")); } + +/// A session near its cap that served one conversation does not open another process for the next: the margin ends the start. +#[test] +fn a_start_with_no_cap_left_after_its_first_process_opens_no_second() { + let rig = Rig::new(vec![direct(1, AQUA_DM, "@aqua:chat", AQUA_ASK), delivery(2, "swarm", "@m:chat", "public asks about oak")], &[says(&[]), says(&[]), says(&[])]); + rig.cap.set(u64::try_from(ROTATE_MARGIN_S).unwrap()); + assert_eq!(failure_of(rig.go()), None); + assert_eq!(rig.model.spawns.borrow().len(), 1, "the second conversation waits for the next start"); +} + +/// An idle session whose only own work is a retell, a ready revision or a notice tells it, though another conversation's reply waits. +#[test] +fn an_idle_session_with_only_its_own_retell_revision_or_notice_work_does_not_yield_for_another_conversations_reply() { + for own in ["a retell", "a ready revision", "a notice", "nothing"] { + let rig = Rig::new(vec![], &[]); + rig.clock.step.set(0); + rig.mind.committed(briefs_in_every_room()); + rig.mind.committed(vec![self_reply_to("r-aqua", &["ask-aqua"], AQUA_WORDS)]); + let (sandbox, scratch) = (rig.sandbox(), rig.scratch()); + let organs = rig.organs(&sandbox, &scratch); + let (control, swarm, run) = (FileSource::new(rig.store()), FileSource::new(rig.swarm_store()), rig.the_run()); + let config = config(); + let mut progress = Progress::default(); + let mut inbox = PersonaInbox::new(&organs, &control, &swarm, &config, &run, &mut progress, session_of(&config, vec![]), 0, String::new()); + match own { + "a retell" => inbox.retell = Some(Retell { batch: vec![], why: Failure::Session, refusals: 0, revision: false }), + "a ready revision" => inbox.revision = Some(Revision { ready: true, ..Revision::default() }), + "a notice" => inbox.notices.push("notice role=organ at=1 text=\"x\"\n".into()), + _ => {} + } + let polled = inbox.poll().unwrap(); + assert_eq!((polled.pending, polled.yield_now), (own != "nothing", own == "nothing"), "{own}"); + } +}