persona: the mind says whether a heard reply took; tests kill the keyless read and the lost resolution
hear_replies attempts each resolution and untell asks the mind which replies are still in force, so an Ok/Err reading of the admission is no second opinion. The out-of-force test holds replies past the first line so a read by kind alone would retell the resolved one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8ab5959d46
commit
987c6ec74d
2 changed files with 21 additions and 14 deletions
|
|
@ -1215,7 +1215,7 @@ impl<'a, C: ControlSource, S: ControlSource> PersonaInbox<'a, C, S> {
|
|||
}
|
||||
|
||||
/// Replies that were told and not answered are told again: the line failed, was stale or was refused. A reply no longer in force
|
||||
/// (resolved by someone, or by a resolution whose answer was lost) leaves for good: it stays in `told_replies`, so no read returns it.
|
||||
/// (resolved by her answer, by someone else, or by a resolution whose answer was lost) leaves for good: it stays in `told_replies`, so no read returns it.
|
||||
fn untell(&mut self, replies: Vec<HeardReply>) {
|
||||
for reply in replies {
|
||||
if matches!(reply_in_force(self.organs.mind, &reply.id), Ok(false)) {
|
||||
|
|
@ -1425,8 +1425,8 @@ impl<C: ControlSource, S: ControlSource> SessionInbox for PersonaInbox<'_, C, S>
|
|||
match step {
|
||||
Step::Answered(started, intent, opened) => {
|
||||
// The result answers the replies its lines carried: they are heard now, and only now.
|
||||
let unresolved = hear_replies(self.organs, self.run, heard, started.as_ref().map(|(_, follow_up)| follow_up));
|
||||
self.untell(unresolved);
|
||||
hear_replies(self.organs, self.run, &heard, started.as_ref().map(|(_, follow_up)| follow_up));
|
||||
self.untell(heard);
|
||||
// What she admitted this turn is hers to settle or take on for the rest of the session, though no line showed its id.
|
||||
self.seen.add(Shown { people: Vec::new(), open: opened });
|
||||
// The one ack rule: through the highest answered seq, never past a delivery still told
|
||||
|
|
@ -1838,10 +1838,13 @@ fn unheard_replies(mind: &dyn MindPort, told: &BTreeSet<String>, waiting: &[Hear
|
|||
}
|
||||
|
||||
/// The result that answers a line resolves each reply the line carried, one by one: Answered by the follow-up her answer's
|
||||
/// `start_self_run` admitted, else Recorded as heard. A reply resolved is out of force, so no later process tells it. A reply whose
|
||||
/// resolution fails does not hold the others hostage: it is returned, to be told again if it is still in force.
|
||||
fn hear_replies(organs: &Organs, run: &PipelineRef, heard: Vec<HeardReply>, follow_up: Option<&PipelineRef>) -> Vec<HeardReply> {
|
||||
heard.into_iter().filter(|reply| resolve_reply(organs, run, reply, follow_up).is_err()).collect()
|
||||
/// `start_self_run` admitted, else Recorded as heard. A resolved reply is out of force, so no later process tells it. Whether it took is
|
||||
/// read from the mind (`reply_in_force`, in `untell`), not from this answer: a reply whose resolution failed stays in force and is told
|
||||
/// again, one that someone else resolved, or whose answer was lost, is not; and one failure holds no other reply hostage.
|
||||
fn hear_replies(organs: &Organs, run: &PipelineRef, heard: &[HeardReply], follow_up: Option<&PipelineRef>) {
|
||||
for reply in heard {
|
||||
let _ = resolve_reply(organs, run, reply, follow_up);
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_reply(organs: &Organs, run: &PipelineRef, reply: &HeardReply, follow_up: Option<&PipelineRef>) -> Result<()> {
|
||||
|
|
@ -1857,10 +1860,8 @@ fn resolve_reply(organs: &Organs, run: &PipelineRef, reply: &HeardReply, follow_
|
|||
rationale: "Her Persona heard this reply of her Self in a turn and answered it.".into(),
|
||||
resolved_on: date(organs.clock.now()),
|
||||
});
|
||||
match organs.mind.admit(AGENT, label_of(run), vec![resolution])? {
|
||||
PipelineAdmissionOutcome::Committed { .. } | PipelineAdmissionOutcome::AlreadyAdmitted { .. } => Ok(()),
|
||||
_ => bail!("the reply could not be resolved"),
|
||||
}
|
||||
organs.mind.admit(AGENT, label_of(run), vec![resolution])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether the reply is still in force in her mind.
|
||||
|
|
|
|||
|
|
@ -4483,7 +4483,9 @@ fn a_refused_result_leaves_its_replies_in_force_and_they_are_told_again() {
|
|||
fn a_reply_put_out_of_force_after_it_was_told_leaves_for_good_and_holds_no_other_reply_hostage() {
|
||||
let rig = Rig::new(vec![operator_says(1, "hello")], &[says(&[]), says(&[]), says(&[]), says(&[])]);
|
||||
rig.mind.committed(her_self_asked());
|
||||
rig.mind.committed(vec![self_reply("self-r-r1", "One."), self_reply("self-r-r2", "Two.")]);
|
||||
// Five long replies after the first: the first line cannot hold them all, so some wait for a later line while the first is untold.
|
||||
let long = "x".repeat(3900);
|
||||
rig.mind.committed(vec![self_reply("self-r-r1", "One."), self_reply("self-r-r2", &long), self_reply("self-r-r3", &long), self_reply("self-r-r4", &long), self_reply("self-r-r5", &long), self_reply("self-r-r6", &long)]);
|
||||
rig.clock.step.set(1);
|
||||
rig.model.linger.set(true);
|
||||
rig.open_the_run();
|
||||
|
|
@ -4512,10 +4514,14 @@ fn a_reply_put_out_of_force_after_it_was_told_leaves_for_good_and_holds_no_other
|
|||
assert_eq!(failure_of(rig.start_over(&timeline, &config())), None);
|
||||
let lines = told(&rig);
|
||||
let told_of = |label: &str| all_replies(&lines).iter().filter(|line| line.contains(&reply_id(label))).count();
|
||||
assert_eq!((told_of("self-r-r1"), told_of("self-r-r2")), (1, 1), "each told once: {lines:?}");
|
||||
for n in 1..=6 {
|
||||
assert_eq!(told_of(&format!("self-r-r{n}")), 1, "r{n} told once: {}", lines.len());
|
||||
}
|
||||
assert!(lines.len() >= 3, "later lines followed: {lines:?}");
|
||||
assert_eq!(outcome_of(&rig, &reply_id("self-r-r1")), Some(ResolutionOutcome::Recorded { reason: "elsewhere".into() }));
|
||||
assert_eq!(outcome_of(&rig, &reply_id("self-r-r2")), heard_outcome(), "the reply beside it was heard");
|
||||
for n in 2..=6 {
|
||||
assert_eq!(outcome_of(&rig, &reply_id(&format!("self-r-r{n}"))), heard_outcome(), "r{n}, beside it or on a later line, was heard");
|
||||
}
|
||||
}
|
||||
|
||||
/// A reply that reaches a session with no chat pending is read at the next interval, told on the line it makes, and resolved.
|
||||
|
|
|
|||
Loading…
Reference in a new issue