huginn-mind: replay detection compares provenance
Whoever wrote a batch first decided its attribution: a native writer of a batch already imported was told AlreadyAdmitted and the record kept naming the source, and an import of a batch another source landed read the same. A9 now treats a stored receipt as a replay only when its imported_from equals the candidate's; otherwise the batch meets its own identities at A10 (IdentityCollision). A source naming this mind in another case is another mind, as the instance grammar already rules, and is pinned by a test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1533755425
commit
905c293396
2 changed files with 37 additions and 1 deletions
|
|
@ -183,7 +183,11 @@ impl<S: MindStore> Mind<S> {
|
|||
check(&docs, staged, &mind, import.as_ref())?;
|
||||
}
|
||||
// A9
|
||||
if let Some(existing) = receipt::replay(self, &candidate)? {
|
||||
// The same batch under another source (or none) is no replay: the first writer's attribution
|
||||
// stands, so the later asker meets the identities it collides with at A10.
|
||||
if let Some(existing) = receipt::replay(self, &candidate)?
|
||||
&& existing.provenance.imported_from == candidate.provenance.imported_from
|
||||
{
|
||||
return Ok(PipelineAdmissionOutcome::AlreadyAdmitted { receipt_id: existing.receipt_id });
|
||||
}
|
||||
// A10
|
||||
|
|
|
|||
|
|
@ -582,3 +582,35 @@ fn an_import_lands_only_under_a_root_that_only_imports_have_written() {
|
|||
MindRefusal::ImportOutsideCampaign { root: CAMPAIGN.into() }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_replay_under_another_source_is_no_replay() {
|
||||
let mut recv = receiver();
|
||||
let history = vec![campaign(&[REPO]), question("Q7", &["A", "B"], "A")];
|
||||
let q7 = id("question", "Q7");
|
||||
committed(import_from_kiko(&mut recv, history.clone()));
|
||||
let recorded = |mind: &Mind<MemoryStore>| view_of(mind, K::Question, &q7).admission.provenance.imported_from.map(|source| source.instance.0);
|
||||
assert_eq!(recorded(&recv).as_deref(), Some("kiko"));
|
||||
let before = head(&recv);
|
||||
|
||||
// The native writer of the same batch is not told it was already admitted, and the record
|
||||
// keeps naming the source that wrote it.
|
||||
assert!(matches!(refusal(admit(&mut recv, history.clone())), MindRefusal::IdentityCollision { .. }));
|
||||
// Nor does another source take the first one's place.
|
||||
let other = import_as(&mut recv, source_of("nibu", vec![]), history);
|
||||
assert!(matches!(refusal(other), MindRefusal::IdentityCollision { .. }));
|
||||
assert_eq!(recorded(&recv).as_deref(), Some("kiko"));
|
||||
assert_eq!(head(&recv), before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_import_naming_a_case_variant_of_this_mind_names_another_mind() {
|
||||
// Two minds whose names differ only in case are two minds (fixtures::CASED_INSTANCE).
|
||||
let mut mind = opened(MemoryStore::new(), INSTANCE);
|
||||
committed(admit(&mut mind, vec![instance(INSTANCE)]));
|
||||
committed(import_as(&mut mind, source_of(CASED_INSTANCE, vec![]), vec![campaign(&[REPO])]));
|
||||
assert_eq!(
|
||||
refusal(import_as(&mut mind, source_of(INSTANCE, vec![]), vec![question("Q1", &["A", "B"], "A")])),
|
||||
MindRefusal::ImportFromSelf
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue