Add missing coverage for the play authority's admission gates (PA.f27, PA.f29, PA.f30)

Test-only. No production code changes: nothing here found a real bug.

PA.f27: require_ruler's own coverage gaps.
- apply_effect_re_decides_the_ruler_for_the_owner_too: a forged
  PatchAdmitted under the owner (CallerId::Principal), carrying a Ruled
  fact, is refused at apply exactly as it is for an elaborator. Kills an
  exemption for Principal in apply_effect's re-decision.
- require_ruler_catches_a_mint_past_the_first_operation: a harmless
  Relocate at index 0 followed by a Mint at index 1, from a non-Play
  author, is refused at index 1. Kills a scan narrowed to .take(1).
- require_ruler_reports_every_ruled_fact: two Ruled facts from a non-Play
  author are both reported. Kills a check narrowed to the first fact.

PA.f29: the evidence gate, extended and carried to the document path.
- the_evidence_gate_is_unchanged_for_every_author now also covers the
  owner in Draft (the seed's caller), the owner in Active with a real
  boundary answer, an answered elaborator, and a consumer on its own
  mirror. Each pair proves an uncited Canonical fact or Admit is refused
  (FactWithoutEvidence / AdmitWithoutEvidence) and the same content, cited,
  is admitted. The owner/elaborator "cited" patches also seat a fresh
  occupant at the dead end so the Boundary answer they must supply in
  Active is genuinely cleared, rather than masked by AnswerNotSatisfied.
- a_document_carrying_a_mint_is_refused_through_admit_document (in
  consumer.rs): a consumer document carrying a Mint is refused through the
  real decode path, admit_document, not only the in-process as_consumer
  fixture caller other tests use.

PA.f30: confinement and controller edge cases.
- a_consumer_cannot_retire_grant_or_revoke (consumer.rs): Retire,
  GrantAffordance and RevokeAffordance from a consumer are refused.
  Soul: every subject choice available to a consumer is also caught by an
  upstream structural gate (a mirror can never hold a grant or be retired;
  no non-mirror subject can ever bind to a consumer's own ground), so this
  specific narrowing — confine_to_ground confining only for an elaborator
  jurisdiction, not a consumer — is not independently killable from the
  consumer side. The refusal itself is real and the finding calls for the
  test, so it is kept; the redundancy is recorded in the test's own
  comment rather than dropped silently.
- a_revoke_cannot_leave_a_human_subject_mute: the last-verb refusal for
  RevokeAffordance holds for a Human-controlled subject exactly as for any
  other controller kind.
- retired_expects_no_caller: a direct unit pin that
  ControllerAssignment::Retired.expected_caller() is None.

All three production-touching mutations described by the finding (the two
require_ruler scans, and a hypothetical Human exemption on the last-verb
check) were applied by hand, confirmed to fail the new test, and reverted;
git status is clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-22 05:56:20 +02:00
commit c97fad2483
2 changed files with 625 additions and 0 deletions

View file

@ -1379,6 +1379,81 @@ mod tests {
}
}
/// PA.f30a: `Retire`, `GrantAffordance` and `RevokeAffordance` are
/// authority over a subject's standing, not structure over ground, and a
/// consumer names no ground at all: `confine_to_ground` refuses all
/// three unconditionally, the same as for an elaborator.
///
/// Every subject choice here is also caught by an upstream structural
/// gate that runs before `confine_to_ground` ever sees the patch: a
/// mirror can never hold a grant at all (`GrantsOutsideControl`) or be
/// retired (`RetiresAMirror`), and no non-mirror subject can ever bind
/// to a consumer's own ground, so `operation_ground`'s defensive
/// fallback refuses every one of these choices on its own. Soul: no
/// subject choice exists for a consumer that reaches confine_to_ground's
/// unconditional Retire/Grant/Revoke clause without one of those two
/// gates already having refused it, so this specific narrowing
/// (confining only for an elaborator jurisdiction) is not independently
/// killable from the consumer side. Kept because the finding calls for
/// it and the refusal itself is real; the redundancy is reported rather
/// than silently dropped.
#[test]
fn a_consumer_cannot_retire_grant_or_revoke() {
let directory = tempfile::tempdir().unwrap();
let (mut kernel, mirror) = draft_kernel(directory.path(), "GroundlessAuthority");
let speak = speak_entry(&kernel);
let convene = Ref::Existing(
*kernel
.state
.affordance_catalog
.iter()
.find(|(_, entry)| entry.kind.0 == "convene")
.map(|(id, _)| id)
.expect("the fixture world declares convene"),
);
// `local` is an ordinary Ghostlight subject, not the consumer's own
// mirror. `local` starts holding only `speak`, so `GrantAffordance`
// names `convene` (not yet held, so not a no-op grant) before the
// owner admits that same grant for real, which leaves `local`
// holding two verbs so the later `RevokeAffordance` of `speak`
// cannot instead be caught by the unrelated last-verb rule.
let retire = as_consumer(
&mut kernel,
operations(vec![ComponentOp::Retire {
subject: Ref::Existing(mirror.local),
}]),
);
assert!(outside(&retire), "{retire:?}");
let grant = as_consumer(
&mut kernel,
operations(vec![ComponentOp::GrantAffordance {
subject: Ref::Existing(mirror.local),
affordance: convene.clone(),
}]),
);
assert!(outside(&grant), "{grant:?}");
let snapshot = kernel.snapshot().unwrap();
submit_owner(
&mut kernel,
&snapshot,
operations(vec![ComponentOp::GrantAffordance {
subject: Ref::Existing(mirror.local),
affordance: convene,
}]),
);
let revoke = as_consumer(
&mut kernel,
operations(vec![ComponentOp::RevokeAffordance {
subject: Ref::Existing(mirror.local),
affordance: speak,
}]),
);
assert!(outside(&revoke), "{revoke:?}");
}
#[test]
fn a_consumer_admits_and_consumes_inside_its_own_custody() {
let directory = tempfile::tempdir().unwrap();
@ -1635,6 +1710,55 @@ mod tests {
task.await.unwrap();
}
/// PA.f29b: a consumer document carrying a `Mint` is refused through the
/// real decode path — `admit_document`, past the transport gate and the
/// kernel's own `submit`, not only through the in-process `as_consumer`
/// fixture caller other tests use.
#[tokio::test]
async fn a_document_carrying_a_mint_is_refused_through_admit_document() {
let directory = tempfile::tempdir().unwrap();
let (mailbox, task, port, mirror, world_id) = mirror_mailbox(directory.path()).await;
let before = mailbox.snapshot().await.unwrap();
let mint_patch = WorldPatch {
declarations: Vec::new(),
operations: vec![ComponentOp::Mint {
holder: Ref::Existing(mirror.first),
resource: Ref::Existing(mirror.grain),
qty: Quantity(3),
}],
evidence: Vec::new(),
};
let bytes = encode_document(
&build_document(
world_id,
CONSUMER,
SECRET,
"mint-batch",
before.revision,
None,
&mint_patch,
)
.unwrap(),
)
.unwrap();
let receipt = admit_document(&port, &registry(), &bytes).await;
assert!(
matches!(&receipt.outcome, ConsumerOutcome::Refused { gate, mismatches }
if *gate == ConsumerRefusal::Structural
&& mismatches.contains(&Mismatch::RuledWithoutAuthority {
site: patch::Site::Operation(0),
})),
"{:?}",
receipt.outcome
);
assert_eq!(mailbox.snapshot().await.unwrap().revision, before.revision);
drop(port);
drop(mailbox);
task.await.unwrap();
}
// ---- phase -------------------------------------------------------------
#[test]

View file

@ -16527,6 +16527,333 @@ mod clock_tests {
},
)
.expect("an evidenced admit is admitted for the play authority too");
// PA.f29a: the owner in Draft — the seed's caller.
{
let seed_directory = tempfile::tempdir().unwrap();
let (mut seed_kernel, _) = WorldKernel::create(
seed_directory.path().join("world.cc"),
creation(CommandId::new(), "OwnerDraftEvidence"),
&auth_principal(owner()),
)
.expect("a created world");
let fact = |evidence: &str| {
Declaration::Fact(FactDeclaration {
handle: DraftHandle::new("draft-fact"),
label: "The Draft Count".into(),
statement: Statement::new("The draft is counted.").unwrap(),
standing: FactStandingRef::Canonical {
evidence: EvidenceRef::new(evidence),
},
})
};
let snapshot = seed_kernel.snapshot().unwrap();
let uncited = seed_kernel
.submit(
command(
&snapshot,
CommandId::new(),
CallerId::Principal(owner()),
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: vec![fact("vault:draft-uncited")],
operations: Vec::new(),
evidence: Vec::new(),
},
},
),
&auth_principal(owner()),
)
.unwrap_err();
assert!(
matches!(&uncited, KernelError::PatchRejected(set)
if set.contains(&Mismatch::FactWithoutEvidence {
handle: DraftHandle::new("draft-fact"),
})),
"{uncited:?}"
);
let snapshot = seed_kernel.snapshot().unwrap();
seed_kernel
.submit(
command(
&snapshot,
CommandId::new(),
CallerId::Principal(owner()),
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: vec![fact("vault:draft-cited")],
operations: Vec::new(),
evidence: vec![EvidenceRef::new("vault:draft-cited")],
},
},
),
&auth_principal(owner()),
)
.expect("a cited canonical fact is admitted for the owner in Draft");
}
// PA.f29a: the owner in Active, with a real answer. Its own
// `clock_kernel`, so the boundary this answers is not the one the
// Play blocks above already left cleared. The cited patch also
// seats a fresh occupant at the dead end: what actually clears an
// `UnelaboratedDestination` boundary ("a commit clears exactly the
// boundary it answers"), so the citation succeeding is not
// masked by an `AnswerNotSatisfied` refusal.
{
let directory = tempfile::tempdir().unwrap();
let (mut kernel, clockwork, _) = clock_kernel(directory.path(), "OwnerActiveEvidence");
let answered = dead_end_boundary(&kernel);
let speak = super::tests::speak_entry(&kernel);
let fact = |evidence: &str| {
Declaration::Fact(FactDeclaration {
handle: DraftHandle::new("active-fact"),
label: "The Active Count".into(),
statement: Statement::new("The active count stands.").unwrap(),
standing: FactStandingRef::Canonical {
evidence: EvidenceRef::new(evidence),
},
})
};
let error = submit_as(
&mut kernel,
CallerId::Principal(owner()),
CommandBody::AdmitPatch {
answers: Some(PatchAnswer::Boundary(answered.clone())),
patch: WorldPatch {
declarations: vec![fact("vault:active-uncited")],
operations: Vec::new(),
evidence: Vec::new(),
},
},
)
.unwrap_err();
assert!(
matches!(&error, KernelError::PatchRejected(set)
if set.contains(&Mismatch::FactWithoutEvidence {
handle: DraftHandle::new("active-fact"),
})),
"{error:?}"
);
submit_as(
&mut kernel,
CallerId::Principal(owner()),
CommandBody::AdmitPatch {
answers: Some(PatchAnswer::Boundary(answered)),
patch: WorldPatch {
declarations: vec![
fact("vault:active-cited"),
Declaration::Subject(SubjectDeclaration {
handle: DraftHandle::new("wanderer"),
label: "The Owner's Wanderer".into(),
kind: SubjectKind::Person,
controller: NewController::NarrativePersona,
affordances: BTreeSet::from([speak]),
position: Some(Ref::Existing(clockwork.dead_end)),
}),
],
operations: Vec::new(),
evidence: vec![EvidenceRef::new("vault:active-cited")],
},
},
)
.expect("a cited canonical fact is admitted for the owner in Active");
}
// PA.f29a: an answered elaborator. Its own `clock_kernel` for the
// same reason. The occupant the cited patch seats at the dead end
// is inside the elaborator's own `PlaceSubtree` jurisdiction, so
// `confine_to_ground` admits it the same as the fact.
{
let directory = tempfile::tempdir().unwrap();
let (mut kernel, clockwork, _) =
clock_kernel(directory.path(), "ElaboratorEvidence");
let answered = dead_end_boundary(&kernel);
let speak = super::tests::speak_entry(&kernel);
let caller = elaborator(JurisdictionKey::PlaceSubtree(clockwork.dead_end));
let fact = |evidence: &str| {
Declaration::Fact(FactDeclaration {
handle: DraftHandle::new("elaborator-fact"),
label: "The Elaborator Count".into(),
statement: Statement::new("The elaborator count stands.").unwrap(),
standing: FactStandingRef::Canonical {
evidence: EvidenceRef::new(evidence),
},
})
};
let error = submit_as(
&mut kernel,
caller.clone(),
CommandBody::AdmitPatch {
answers: Some(PatchAnswer::Boundary(answered.clone())),
patch: WorldPatch {
declarations: vec![fact("vault:elaborator-uncited")],
operations: Vec::new(),
evidence: Vec::new(),
},
},
)
.unwrap_err();
assert!(
matches!(&error, KernelError::PatchRejected(set)
if set.contains(&Mismatch::FactWithoutEvidence {
handle: DraftHandle::new("elaborator-fact"),
})),
"{error:?}"
);
submit_as(
&mut kernel,
caller,
CommandBody::AdmitPatch {
answers: Some(PatchAnswer::Boundary(answered)),
patch: WorldPatch {
declarations: vec![
fact("vault:elaborator-cited"),
Declaration::Subject(SubjectDeclaration {
handle: DraftHandle::new("wanderer"),
label: "The Elaborator's Wanderer".into(),
kind: SubjectKind::Person,
controller: NewController::NarrativePersona,
affordances: BTreeSet::from([speak]),
position: Some(Ref::Existing(clockwork.dead_end)),
}),
],
operations: Vec::new(),
evidence: vec![EvidenceRef::new("vault:elaborator-cited")],
},
},
)
.expect("a cited canonical fact is admitted for an answered elaborator");
}
// PA.f29a: a consumer, on its own mirror. A consumer cannot canonize
// (confine_to_ground refuses `Canonical` for consumer ground
// outright), so this exercises the same gate through `Admit`
// instead. In Draft, where `require_answer` asks nothing of anyone,
// so this is the evidence gate alone and not entangled with a
// consumer's inability to ever clear a place-shaped boundary (a
// consumer touches no place at all).
{
let directory = tempfile::tempdir().unwrap();
let mut kernel = WorldKernel::create(
directory.path().join("world.cc"),
creation(CommandId::new(), "ConsumerMirrorEvidence"),
&auth_principal(owner()),
)
.expect("a created world")
.0;
let commons = *kernel
.state
.entities
.iter()
.find(|(_, record)| record.label == "The Commons")
.expect("the declared commons")
.0;
let grain = {
let snapshot = kernel.snapshot().unwrap();
submit_owner(
&mut kernel,
&snapshot,
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: vec![Declaration::Entity(EntityDeclaration {
handle: DraftHandle::new("grain"),
label: "Consumer Grain".into(),
kind: EntityKind::Resource,
container: None,
})],
operations: Vec::new(),
evidence: Vec::new(),
},
},
);
*kernel
.state
.entities
.iter()
.find(|(_, record)| record.label == "Consumer Grain")
.expect("the declared resource")
.0
};
let consumer_id = ConsumerId::of_name("clock-mirror-consumer");
let snapshot = kernel.snapshot().unwrap();
submit_owner(
&mut kernel,
&snapshot,
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: vec![Declaration::Subject(SubjectDeclaration {
handle: DraftHandle::new("mirror"),
label: "The Clockwork Mirror".into(),
kind: SubjectKind::Institution,
controller: NewController::External {
consumer: consumer_id,
},
affordances: BTreeSet::new(),
position: Some(Ref::Existing(commons)),
})],
operations: Vec::new(),
evidence: Vec::new(),
},
},
);
let mirror = *kernel
.state
.subjects
.iter()
.find(|(_, subject)| subject.label == "The Clockwork Mirror")
.expect("the declared mirror")
.0;
let consumer_caller = CallerId::System(SystemCapability::Consumer {
consumer: consumer_id,
});
let error = submit_as(
&mut kernel,
consumer_caller.clone(),
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: Vec::new(),
operations: vec![ComponentOp::Admit {
holder: Ref::Existing(mirror),
resource: Ref::Existing(grain),
qty: Quantity(1),
evidence: EvidenceRef::new("vault:mirror-uncited"),
}],
evidence: Vec::new(),
},
},
)
.unwrap_err();
assert!(
matches!(&error, KernelError::PatchRejected(set)
if set.contains(&Mismatch::AdmitWithoutEvidence { operation: 0 })),
"{error:?}"
);
submit_as(
&mut kernel,
consumer_caller,
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: Vec::new(),
operations: vec![ComponentOp::Admit {
holder: Ref::Existing(mirror),
resource: Ref::Existing(grain),
qty: Quantity(1),
evidence: EvidenceRef::new("vault:mirror-cited"),
}],
evidence: vec![EvidenceRef::new("vault:mirror-cited")],
},
},
)
.expect("a cited admit is admitted for a consumer on its own mirror");
}
}
/// `reduce` decides and `apply_effect` re-decides: a forged
@ -16593,6 +16920,131 @@ mod clock_tests {
assert_ne!(candidate, kernel.state);
}
/// PA.f27a: the owner is a `CallerId::Principal` like any other, and
/// `apply_effect`'s re-decision of `require_ruler` carries no exemption
/// for that variant. A forged `PatchAdmitted` under the owner, carrying
/// a `Ruled` fact the resolver accepts (actor-blind) and that fully
/// satisfies confinement (the owner is unconfined), is still refused at
/// apply. Follows `apply_effect_re_decides_the_ruler`.
#[test]
fn apply_effect_re_decides_the_ruler_for_the_owner_too() {
let directory = tempfile::tempdir().unwrap();
let (kernel, _, _) = clock_kernel(directory.path(), "RedecideRulerOwner");
let answered = dead_end_boundary(&kernel);
let command_id = CommandId::issue();
let patch = ruled_fact_patch("rule");
let resolved = patch::resolve_patch(&kernel.state, command_id, &patch, None, None)
.expect("the patch resolves; the resolver is actor-blind");
let effect = WorldEffect::PatchAdmitted {
answers: Some(PatchAnswer::Boundary(answered)),
resolved,
};
let mut candidate = kernel.state.clone();
let error = apply_effect(
&mut candidate,
command_id,
&CallerId::Principal(owner()),
&effect,
)
.unwrap_err();
assert!(matches!(error, KernelError::Invariant(_)), "{error:?}");
assert_eq!(candidate, kernel.state);
}
/// PA.f27b: `require_ruler` walks every operation, not only the first.
/// A harmless `Relocate` at index 0 followed by a `Mint` at index 1,
/// from a non-`Play` author, is refused at index 1. Kills a scan
/// narrowed to `.take(1)`.
#[test]
fn require_ruler_catches_a_mint_past_the_first_operation() {
let directory = tempfile::tempdir().unwrap();
let (mut kernel, clockwork, _) = clock_kernel(directory.path(), "RulerScansEveryOp");
let grain = resource_named(&kernel, "Winter Grain");
let stair = *kernel
.state
.edges
.iter()
.find(|(_, record)| record.label() == "The Yard Stair")
.expect("the declared route")
.0;
let snapshot = kernel.snapshot().unwrap();
let Err(error) = kernel.submit(
command(
&snapshot,
CommandId::new(),
CallerId::Principal(owner()),
operations(vec![
ComponentOp::Relocate {
subject: Ref::Existing(clockwork.reeve),
via: Ref::Existing(stair),
},
ComponentOp::Mint {
holder: Ref::Existing(clockwork.reeve),
resource: Ref::Existing(grain),
qty: Quantity(4),
},
]),
),
&auth_principal(owner()),
) else {
panic!("a non-Play Mint past the first operation was admitted");
};
assert!(
matches!(&error, KernelError::PatchRejected(set)
if set.contains(&Mismatch::RuledWithoutAuthority {
site: patch::Site::Operation(1),
})),
"{error:?}"
);
}
/// PA.f27c: `require_ruler` reports every `Ruled` declaration, not only
/// the first. Two `Ruled` facts from a non-`Play` author are both
/// reported. Kills a check narrowed to the first ruled fact.
#[test]
fn require_ruler_reports_every_ruled_fact() {
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("world.cc");
let (mut kernel, _) = WorldKernel::create(
&path,
creation(CommandId::new(), "RulerScansEveryFact"),
&auth_principal(owner()),
)
.expect("a created world");
let snapshot = kernel.snapshot().unwrap();
let mut patch = ruled_fact_patch("first-rule");
patch.declarations.push(Declaration::Fact(FactDeclaration {
handle: DraftHandle::new("second-rule"),
label: "The Second Ruling".into(),
statement: Statement::new("Stated by the table, again.").unwrap(),
standing: FactStandingRef::Ruled,
}));
let Err(error) = kernel.submit(
command(
&snapshot,
CommandId::new(),
CallerId::Principal(owner()),
CommandBody::AdmitPatch {
answers: None,
patch,
},
),
&auth_principal(owner()),
) else {
panic!("two non-Play Ruled facts were admitted");
};
assert!(
matches!(&error, KernelError::PatchRejected(set)
if set.contains(&Mismatch::RuledWithoutAuthority {
site: patch::Site::Declaration(DraftHandle::new("first-rule")),
}) && set.contains(&Mismatch::RuledWithoutAuthority {
site: patch::Site::Declaration(DraftHandle::new("second-rule")),
})),
"{error:?}"
);
}
/// P3.5, replay half, folded with a ruled fact: reopening the journal
/// reproduces both across the digest.
#[test]
@ -17107,6 +17559,55 @@ mod clock_tests {
);
}
/// PA.f30b: the last-verb refusal for `RevokeAffordance` (P5.2) holds for
/// a `Human`-controlled subject exactly as it does for any other
/// controller kind. Kills an exemption that only checks non-`Human`
/// subjects.
#[test]
fn a_revoke_cannot_leave_a_human_subject_mute() {
let directory = tempfile::tempdir().unwrap();
let (mut kernel, _, active) = clock_kernel(directory.path(), "RevokeHumanMute");
let human = active
.subjects
.iter()
.find(|subject| subject.controller_mode == Some(ControllerMode::Human))
.expect("the fixture world declares a human-controlled subject");
let speak_affordance = *human
.affordances
.iter()
.next()
.expect("the human subject holds at least the kernel speak grant");
let error = submit_as(
&mut kernel,
play_caller(),
CommandBody::AdmitPatch {
answers: None,
patch: WorldPatch {
declarations: Vec::new(),
operations: vec![ComponentOp::RevokeAffordance {
subject: Ref::Existing(human.id),
affordance: Ref::Existing(speak_affordance),
}],
evidence: Vec::new(),
},
},
)
.unwrap_err();
assert!(
matches!(&error, KernelError::PatchRejected(set)
if set.contains(&Mismatch::WouldLeaveSubjectMute { operation: 0 })),
"{error:?}"
);
}
/// PA.f30c: `ControllerAssignment::Retired.expected_caller()` is `None`
/// — a direct unit pin so an accidental `Some` for `Retired` is caught
/// here rather than only transitively through a submission test.
#[test]
fn retired_expects_no_caller() {
assert_eq!(ControllerAssignment::Retired.expected_caller(), None);
}
/// P5.1: `confine_to_ground` refuses `GrantAffordance` for a confined
/// author unconditionally, even for a subject this same patch declares
/// and places inside the elaborator's own jurisdiction.