admission: skip the status check only for a citation into another campaign; drop the unreachable run-claims skip; a test for each live site

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-11-08 21:54:59 +01:00
commit f4f857a7da
2 changed files with 102 additions and 9 deletions

View file

@ -538,8 +538,9 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug, import: Option<&ImportSo
use PipelineKind as K;
let key = staged.key.as_str();
let external: &[PipelineRef] = import.map_or(&[], |source| source.external.as_slice());
// An import replays a batch its source admitted at its own ordinal: a referent's status here may
// have moved on since (another campaign's import carried the resolution), so no rule judges it.
// An import replays the giver's order within a campaign, so a same-campaign referent has its status at
// the source ordinal. Only a citation into another campaign drifts (that campaign's import may have
// carried a later resolution), so only a cross-campaign citation is not judged by current status.
let historic = import.is_some();
if let D::Resolution(resolution) = &staged.document
&& matches!(resolution.outcome, ResolutionOutcome::Withdrawn { .. })
@ -630,7 +631,7 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug, import: Option<&ImportSo
return Err(MindRefusal::RepoNotInCampaign { repo: spec.repo.0.clone() });
}
for ruling in &spec.rulings {
if !historic && !docs.in_force(K::Ruling, &ruling.0) {
if !(historic && root_and_local(&ruling.0).0 != root) && !docs.in_force(K::Ruling, &ruling.0) {
return Err(MindRefusal::CitesResolvedDocument { kind: K::Ruling, id: ruling.0.clone() });
}
}
@ -656,7 +657,7 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug, import: Option<&ImportSo
}
return Err(MindRefusal::CutReportWithoutSpec { report: key.into() });
};
if !historic && !docs.in_force(K::CutSpec, &report.cut_spec.0) {
if !(historic && root_and_local(&report.cut_spec.0).0 != root) && !docs.in_force(K::CutSpec, &report.cut_spec.0) {
return Err(MindRefusal::CitesResolvedDocument { kind: K::CutSpec, id: report.cut_spec.0.clone() });
}
if report.repo != spec.repo {
@ -765,7 +766,7 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug, import: Option<&ImportSo
// rule.
D::Run(run) => {
let replay = docs.in_image(K::Run, key) == Some(&staged.document);
run_rule(docs, key, run, replay, historic)
run_rule(docs, key, run, replay)
}
D::Memory(memory) => consolidation_rule(docs, key, memory),
D::FollowUp(_) | D::Instance(_) | D::Pulse(_) | D::Reply(_) | D::Thought(_) | D::Research(_) | D::Idea(_) => Ok(()),
@ -775,9 +776,9 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug, import: Option<&ImportSo
/// The one rule for a run opening or reinstated by withdrawing its close: every
/// claim in force (a replay of an admitted run owes none) and held by no other
/// live run, then at most one live run of hers per instance and turn.
fn run_rule(docs: &Docs<'_>, run_key: &str, run: &PipelineRun, replay: bool, historic: bool) -> Result<(), MindRefusal> {
fn run_rule(docs: &Docs<'_>, run_key: &str, run: &PipelineRun, replay: bool) -> Result<(), MindRefusal> {
for claim in &run.claims {
if !replay && !historic && !docs.in_force(claim.kind, &claim.id.0) {
if !replay && !docs.in_force(claim.kind, &claim.id.0) {
return Err(MindRefusal::CitesResolvedDocument { kind: claim.kind, id: claim.id.0.clone() });
}
if let Some(holder) = docs.claim_holder(run_key, claim) {
@ -976,11 +977,11 @@ fn resolution_rule(docs: &Docs<'_>, resolution: &PipelineResolution, historic: b
&& reinstating.subject.kind == K::Run
&& let Some(D::Run(run)) = docs.find(K::Run, &reinstating.subject.id.0)
{
run_rule(docs, &reinstating.subject.id.0, run, false, historic)?;
run_rule(docs, &reinstating.subject.id.0, run, false)?;
}
for (role, referent) in outcome_citations(&resolution.outcome) {
// The last memory a night was shown may have been consumed by that night's own batches.
if role != CitationRole::DayThrough && !historic && !docs.in_force(referent.kind, &referent.id.0) {
if role != CitationRole::DayThrough && !(historic && root_and_local(&referent.id.0).0 != root_and_local(subject_id).0) && !docs.in_force(referent.kind, &referent.id.0) {
return Err(MindRefusal::CitesResolvedDocument { kind: referent.kind, id: referent.id.0 });
}
}

View file

@ -737,3 +737,95 @@ fn replay_root(giver: &Mind<MemoryStore>, receiver: &mut Mind<MemoryStore>, root
})
.collect()
}
fn ruling_of(root: &str, label: &str) -> D {
let mut value = ruling(label);
value.campaign = slug(root);
D::Ruling(value)
}
fn ruling_ref(root: &str, label: &str) -> PipelineRef {
r(K::Ruling, &format!("{root}:ruling:{label}"))
}
fn spec_ref(root: &str, revision: u32) -> PipelineRef {
r(K::CutSpec, &format!("{root}:cut_spec:cut-1.r{revision}"))
}
fn spec_of(root: &str, revision: u32) -> D {
let mut value = cut_spec("1", revision);
value.campaign = slug(root);
D::CutSpec(value)
}
fn report_citing(spec: &PipelineRef) -> D {
let mut value = cut_report("1", 1);
value.cut_spec = spec.id.clone();
D::CutReport(value)
}
fn spec_citing(ruling: &PipelineRef) -> D {
let mut value = cut_spec("2", 1);
value.rulings = vec![ruling.id.clone()];
D::CutSpec(value)
}
/// A receiver holding `root` whole, in which `old` is superseded by `new` (both of that root).
fn receiver_with_superseded(root: &str, opening: Vec<D>, old: PipelineRef, new: PipelineRef) -> Mind<MemoryStore> {
let mut mind = receiver();
let mut first = vec![if root == CAMPAIGN { campaign(&[REPO]) } else { other_campaign() }];
first.extend(opening);
committed(import(&mut mind, first, vec![]));
committed(import(&mut mind, vec![resolution(old, superseded(&[new]))], vec![]));
mind
}
#[test]
fn an_imported_report_may_cite_another_campaigns_spec_superseded_since() {
let (old, new) = (spec_ref(OTHER_ROOT, 1), spec_ref(OTHER_ROOT, 2));
let mut mind = receiver_with_superseded(OTHER_ROOT, vec![spec_of(OTHER_ROOT, 1), spec_of(OTHER_ROOT, 2)], old.clone(), new);
committed(import(&mut mind, vec![campaign(&[REPO])], vec![]));
let (_, writes) = committed(import(&mut mind, vec![report_citing(&old)], vec![]));
assert_eq!(writes.len(), 1);
}
#[test]
fn an_imported_report_citing_its_own_campaigns_superseded_spec_is_refused() {
let (old, new) = (spec_ref(CAMPAIGN, 1), spec_ref(CAMPAIGN, 2));
let mut mind = receiver_with_superseded(CAMPAIGN, vec![spec_of(CAMPAIGN, 1), spec_of(CAMPAIGN, 2)], old.clone(), new);
assert_eq!(
refusal(import(&mut mind, vec![report_citing(&old)], vec![])),
MindRefusal::CitesResolvedDocument { kind: K::CutSpec, id: old.id.0 }
);
}
#[test]
fn an_imported_spec_may_cite_another_campaigns_ruling_superseded_since() {
let (old, new) = (ruling_ref(OTHER_ROOT, "R1"), ruling_ref(OTHER_ROOT, "R2"));
let mut mind = receiver_with_superseded(OTHER_ROOT, vec![ruling_of(OTHER_ROOT, "R1"), ruling_of(OTHER_ROOT, "R2")], old.clone(), new);
committed(import(&mut mind, vec![campaign(&[REPO])], vec![]));
let (_, writes) = committed(import(&mut mind, vec![spec_citing(&old)], vec![]));
assert_eq!(writes.len(), 1);
}
#[test]
fn an_imported_spec_citing_its_own_campaigns_superseded_ruling_is_refused() {
let (old, new) = (ruling_ref(CAMPAIGN, "R1"), ruling_ref(CAMPAIGN, "R2"));
let mut mind = receiver_with_superseded(CAMPAIGN, vec![ruling_of(CAMPAIGN, "R1"), ruling_of(CAMPAIGN, "R2")], old.clone(), new);
assert_eq!(
refusal(import(&mut mind, vec![spec_citing(&old)], vec![])),
MindRefusal::CitesResolvedDocument { kind: K::Ruling, id: old.id.0 }
);
}
#[test]
fn an_imported_follow_up_may_not_be_superseded_by_its_own_campaigns_closed_follow_up() {
let q1 = r(K::Question, &id("question", "Q1"));
let mut mind = receiver();
committed(import(&mut mind, vec![campaign(&[REPO]), question("Q1", &["A", "B"], "A"), follow_up("FU-A", q1.clone()), follow_up("FU-B", q1)], vec![]));
committed(import(&mut mind, vec![resolution(fu(CAMPAIGN, "FU-A"), ResolutionOutcome::Recorded { reason: "noted".into() })], vec![]));
assert_eq!(
refusal(import(&mut mind, vec![resolution(fu(CAMPAIGN, "FU-B"), superseded(&[fu(CAMPAIGN, "FU-A")]))], vec![])),
MindRefusal::CitesResolvedDocument { kind: K::FollowUp, id: fu(CAMPAIGN, "FU-A").id.0 }
);
}