huginn-mind: a campaign cannot be moved out of this mind's own instance root
A campaign slugged like the mind shares a root with the instance, its stewardships, memories and thoughts, so its Moved froze and closed all of them. A8 refuses such a Moved (MoveOfInstanceRoot); an import of the same campaign is refused already, the instance's records being native writes under its root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
905c293396
commit
0d4cccc157
4 changed files with 53 additions and 0 deletions
|
|
@ -720,6 +720,11 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug, import: Option<&ImportSo
|
|||
resolution_rule(docs, resolution)?;
|
||||
match &resolution.outcome {
|
||||
ResolutionOutcome::Moved { to } if to == mind => Err(MindRefusal::MoveToSelf),
|
||||
// The campaign's root and the mind's own instance root are one namespace: the freeze would
|
||||
// close the instance's memories, thoughts and stewardships with the campaign.
|
||||
ResolutionOutcome::Moved { .. } if root_and_local(&resolution.subject.id.0).0 == mind.0 => {
|
||||
Err(MindRefusal::MoveOfInstanceRoot { root: mind.0.clone() })
|
||||
}
|
||||
// The giver's resolution: the receiver replays the history the move froze, never the move.
|
||||
ResolutionOutcome::Moved { .. } if import.is_some() => Err(MindRefusal::IncompatibleResolution {
|
||||
subject_kind: resolution.subject.kind,
|
||||
|
|
|
|||
|
|
@ -614,3 +614,28 @@ fn an_import_naming_a_case_variant_of_this_mind_names_another_mind() {
|
|||
MindRefusal::ImportFromSelf
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_campaign_cannot_be_moved_out_of_its_minds_own_instance_root() {
|
||||
// A campaign slugged like this mind shares a root with the instance, its stewardships and its
|
||||
// thoughts: the Moved would freeze all of them.
|
||||
let mut mind = seeded();
|
||||
let D::Campaign(mut named_for_the_mind) = campaign(&[REPO]) else { unreachable!() };
|
||||
named_for_the_mind.slug = slug(INSTANCE);
|
||||
committed(admit(&mut mind, vec![D::Campaign(named_for_the_mind)]));
|
||||
let before = head(&mind);
|
||||
let moving = resolution(r(K::Campaign, &format!("{INSTANCE}:campaign:self")), ResolutionOutcome::Moved { to: slug(RECEIVER) });
|
||||
assert_eq!(refusal(admit(&mut mind, vec![moving])), MindRefusal::MoveOfInstanceRoot { root: INSTANCE.into() });
|
||||
assert_eq!(head(&mind), before);
|
||||
committed(admit(&mut mind, vec![thought("T1", "still writable", &[])]));
|
||||
assert!(matches!(stewardship_status(&mind, INSTANCE), PipelineStatus::InForce));
|
||||
|
||||
// The same campaign cannot arrive by import either: the instance's records are native writes under its root.
|
||||
let mut recv = receiver();
|
||||
let D::Campaign(mut named_for_recv) = campaign(&[REPO]) else { unreachable!() };
|
||||
named_for_recv.slug = slug(RECEIVER);
|
||||
assert_eq!(
|
||||
refusal(import_from_kiko(&mut recv, vec![D::Campaign(named_for_recv)])),
|
||||
MindRefusal::ImportOutsideCampaign { root: RECEIVER.into() }
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ pub enum MindRefusal {
|
|||
ImportFromSelf,
|
||||
/// An import carries a document of `root`, which is no campaign this mind holds or the batch carries, or which this mind holds a native write under.
|
||||
ImportOutsideCampaign { root: String },
|
||||
/// A move names `root`, this mind's own instance: an instance's records are not a campaign's to freeze.
|
||||
MoveOfInstanceRoot { root: String },
|
||||
/// An import lists `id` as external, but this mind (or the batch) holds it.
|
||||
ExternalPresent { id: String },
|
||||
/// The persona document fails the published schema: `path` is the
|
||||
|
|
|
|||
|
|
@ -1697,6 +1697,27 @@
|
|||
"ImportOutsideCampaign"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "A move names `root`, this mind's own instance: an instance's records are not a campaign's to freeze.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"MoveOfInstanceRoot": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"root": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"root"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"MoveOfInstanceRoot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "An import lists `id` as external, but this mind (or the batch) holds it.",
|
||||
"type": "object",
|
||||
|
|
|
|||
Loading…
Reference in a new issue