diff --git a/crates/mind-body/src/self_run.rs b/crates/mind-body/src/self_run.rs index 6fb0af5..a919156 100644 --- a/crates/mind-body/src/self_run.rs +++ b/crates/mind-body/src/self_run.rs @@ -795,17 +795,19 @@ impl<'a> SelfInbox<'a> { Self { mind, start, plan: render_plan(plan_quota, now_ms), told, waiting: Vec::new(), heard, run: run.clone(), carried, echoed: Vec::new(), serving, last_answers, flush_written: false, flushing: false, replies: 0, unadmitted: Vec::new() } } - /// Admits each reply the mind has not taken, in order; one try each. A refusal or an unreachable mind keeps the reply and never ends the session. + /// Admits each reply the mind has not taken, in order; one try each. A refusal or an unreachable mind keeps that reply and every one behind it + /// (the admission ordinal is the order the Persona hears them in, so none may overtake a refused one) and never ends the session. fn admit_unadmitted(&mut self) { let label = self.run.id.0.rsplit(':').next().unwrap_or(&self.run.id.0).to_string(); - let mut kept = Vec::new(); - for reply in std::mem::take(&mut self.unadmitted) { + let mut queue = std::mem::take(&mut self.unadmitted).into_iter(); + for reply in queue.by_ref() { let taken = matches!(self.mind.admit(AGENT, &label, vec![PipelineDocument::Reply(reply.clone())]), Ok(PipelineAdmissionOutcome::Committed { .. } | PipelineAdmissionOutcome::AlreadyAdmitted { .. })); if !taken { - kept.push(reply); + self.unadmitted.push(reply); + break; } } - self.unadmitted = kept; + self.unadmitted.extend(queue); } /// One result of the main thread: with words, one reply answering the briefs the CLI has taken since the last reply (the first 64, in tell diff --git a/crates/mind-body/src/self_run/tests/reply.rs b/crates/mind-body/src/self_run/tests/reply.rs index 0a9244a..0a4e37c 100644 --- a/crates/mind-body/src/self_run/tests/reply.rs +++ b/crates/mind-body/src/self_run/tests/reply.rs @@ -106,3 +106,15 @@ fn a_reply_the_mind_did_not_take_is_tried_once_more_when_the_run_ends() { assert!(ran.outcome.is_ok()); assert_eq!(text_of(&mind, 1), "last words"); } + +#[test] +fn a_refused_piece_of_a_long_result_holds_back_the_pieces_behind_it() { + let mind = live_mind(); + let seen = Seen::of(&mind); + seen.fail_admits.set(1); // the first piece is refused + let long = "w".repeat(REPLY_TEXT_LIMIT * 2 + 1); + let ran = carry_with(&seen, vec![Out(echo()), Out(at("m1", 10)), Out(saying(&long))], Some(0), false); + assert!(ran.outcome.is_ok()); + let ordinals: Vec = (1..=3).map(|n| mind.view(K::Reply, &format!("{INSTANCE}:reply:selfrun-a-r{n}")).unwrap_or_else(|| panic!("piece {n} lost")).admission.ordinal).collect(); + assert!(ordinals.windows(2).all(|w| w[0] < w[1]), "the pieces reached the mind out of order: {ordinals:?}"); +}