diff --git a/crates/mind-body/src/stream.rs b/crates/mind-body/src/stream.rs index 3aae652..dda212c 100644 --- a/crates/mind-body/src/stream.rs +++ b/crates/mind-body/src/stream.rs @@ -415,17 +415,12 @@ fn build_seed(mind: &dyn MindPort, persona: &PersonaRead, since: Option SECTION_MAX_BYTES { - break; - } - used += line.len() + 1; - lines.push(line); - id.into_iter().for_each(&mut cite); - } - format!("Since your last thought:\n{}", lines.join("\n")) + let body: Vec<&str> = fresh.iter().map(|(line, _)| line.as_str()).collect(); + let kept = cap(&format!("Since your last thought:\n{}", body.join("\n")), SECTION_MAX_BYTES); + // Each line is one line (`quoted` escapes newlines), so the lines the bound kept are what the thought read. + let shown = kept.lines().count().saturating_sub(1); + fresh.into_iter().take(shown).filter_map(|(_, id)| id).for_each(&mut cite); + kept } else if let Some((line, id)) = unbidden(mind, &instance, now, u64::try_from(now.timestamp()).unwrap_or(0))? { cite(id); line @@ -511,7 +506,7 @@ fn due(since: Option>, now: DateTime, news: bool) -> bool { fn newest_thought(mind: &dyn MindPort) -> Result>> { let newest = cultnet_rs::Selection { descending: true, limit: Some(1), ..mine(PipelineKind::Thought, mind.instance()) }; - let found = headers(mind, &newest)?; + let PipelinePageItems::Headers(found) = mind.query(&newest)?.items else { bail!("the mind answered documents to a header selection") }; Ok(found.iter().filter_map(|header| DateTime::parse_from_rfc3339(&header.admission.admitted_at).ok()).map(|at| at.with_timezone(&Utc)).max()) } diff --git a/crates/mind-body/src/stream/tests.rs b/crates/mind-body/src/stream/tests.rs index c626b57..ba24dc8 100644 --- a/crates/mind-body/src/stream/tests.rs +++ b/crates/mind-body/src/stream/tests.rs @@ -5,7 +5,7 @@ use std::net::TcpListener; use std::sync::{Arc, Mutex}; use anyhow::{Result, anyhow}; -use chrono::{DateTime, Duration, Utc}; +use chrono::{DateTime, Utc}; use cultnet_rs::Selection; use eureka_pipeline::{MemorySort, PipelineDocument, PipelineKind, PipelineReply, PipelineThought, RunOperator, RunTurn, Slug}; use huginn_mind::{PipelineAdmissionOutcome, PipelineSelectionPage}; @@ -575,6 +575,8 @@ fn a_flood_of_news_fills_the_stimulus_with_whole_lines_newest_first() { assert!(seed.stimulus.len() <= SECTION_MAX_BYTES && seed.stimulus.len() > SECTION_MAX_BYTES - 250, "{}", seed.stimulus.len()); assert!(lines.iter().all(|line| line.starts_with("- You noted") && line.ends_with('"')), "a line was cut: {lines:?}"); assert!(lines.len() > 10 && lines.len() < 30, "{}", lines.len()); + let cited = seed.reads.iter().filter(|read| read.kind == PipelineKind::Memory).count(); + assert!(cited >= lines.len() && cited <= lines.len() + 12, "the thought cites what its stimulus showed: {cited} reads for {} lines", lines.len()); let newest = lines[0]; assert!(newest.contains("\"29 ") || newest.contains("\"59 "), "newest first: {newest}"); }