huginn-mind: a follow-up closes Recorded{reason}, as a finding and a run do

Ruling eureka-body:ruling:brief-closure-recorded.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-31 16:30:28 +01:00
commit fb0ae7540d
2 changed files with 29 additions and 0 deletions

View file

@ -711,6 +711,7 @@ fn matrix(subject: PipelineKind, outcome: &ResolutionOutcome) -> bool {
(K::Finding, O::Deferred { to }) => matches!(to.kind, K::FollowUp | K::CutSpec),
(K::Finding, O::Recorded { .. } | O::Withdrawn { .. }) => true,
(K::FollowUp, O::Fixed { by, .. }) => by.as_ref().is_none_or(|report| report.kind == K::CutReport),
(K::FollowUp, O::Recorded { .. }) => true,
(K::FollowUp, O::Superseded { by }) => all(by, K::FollowUp),
(K::FollowUp, O::Withdrawn { .. }) => true,
(K::Stewardship, O::Superseded { by }) => all(by, K::Stewardship),

View file

@ -1766,6 +1766,34 @@ mod tests {
assert_eq!(open(standing(of_kinds(&[K::Question]))).len(), 2);
}
/// A follow-up answered in the doing closes Recorded: it commits, and the
/// follow-up stops reading as open work. Outcomes the follow-up row does
/// not name stay refused.
#[test]
fn a_follow_up_recorded_is_no_longer_open() {
let mut mind = seeded();
committed(admit(&mut mind, elsewhere("Q1")));
committed(admit(&mut mind, vec![question_n(1)]));
let q1 = id("question", "Q1");
committed(admit(&mut mind, vec![follow_up("FU-1", r(K::Question, &q1)), follow_up("FU-2", r(K::Question, &q1))]));
let open_follow_ups = |mind: &Mind<MemoryStore>| {
let selection = with(with(of_kinds(&[K::FollowUp]), any_of("root", &[CAMPAIGN])), any_of("in_force", &["true"]));
ids(&mind.query(&selection).unwrap())
};
assert_eq!(open_follow_ups(&mind), vec![id("follow_up", "FU-1"), id("follow_up", "FU-2")]);
let fu1 = r(K::FollowUp, &id("follow_up", "FU-1"));
committed(admit(&mut mind, vec![resolution(fu1, ResolutionOutcome::Recorded { reason: "answered: done".into() })]));
assert_eq!(open_follow_ups(&mind), vec![id("follow_up", "FU-2")]);
let fu2 = r(K::FollowUp, &id("follow_up", "FU-2"));
assert!(
admit(&mut mind, vec![resolution(fu2, ResolutionOutcome::Answered { by: r(K::Ruling, &id("ruling", "R1")) })]).is_err(),
"Answered stays a question's outcome"
);
assert_eq!(open_follow_ups(&mind), vec![id("follow_up", "FU-2")]);
}
fn hit(kind: K, key: &str, score: f32) -> (PipelineRef, f32) {
(r(kind, key), score)
}