1use serde::{de::Deserializer, Deserialize, Serialize};
2use std::fmt;
3
4macro_rules! id_type {
5 ($name:ident) => {
6 #[derive(
7 Clone,
8 Copy,
9 Debug,
10 Default,
11 Eq,
12 Hash,
13 Ord,
14 PartialEq,
15 PartialOrd,
16 Serialize,
17 Deserialize,
18 )]
19 pub struct $name(pub u64);
20
21 impl $name {
22 pub const fn new(value: u64) -> Self {
23 Self(value)
24 }
25 }
26
27 impl fmt::Display for $name {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "{}", self.0)
30 }
31 }
32 };
33}
34
35id_type!(EntityId);
36id_type!(AttributeId);
37id_type!(ElementId);
38id_type!(PredicateId);
39id_type!(RuleId);
40id_type!(TupleId);
41id_type!(ReplicaId);
42
43#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
44pub struct PartitionId(pub String);
45
46impl PartitionId {
47 pub fn new(value: impl Into<String>) -> Self {
48 Self(value.into())
49 }
50
51 pub fn as_str(&self) -> &str {
52 &self.0
53 }
54}
55
56impl fmt::Display for PartitionId {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 write!(f, "{}", self.0)
59 }
60}
61
62#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
63pub struct PartitionCut {
64 pub partition: PartitionId,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
66 pub as_of: Option<ElementId>,
67}
68
69impl PartitionCut {
70 pub fn current(partition: impl Into<PartitionId>) -> Self {
71 Self {
72 partition: partition.into(),
73 as_of: None,
74 }
75 }
76
77 pub fn as_of(partition: impl Into<PartitionId>, element: ElementId) -> Self {
78 Self {
79 partition: partition.into(),
80 as_of: Some(element),
81 }
82 }
83
84 pub fn is_current(&self) -> bool {
85 self.as_of.is_none()
86 }
87}
88
89impl fmt::Display for PartitionCut {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self.as_of {
92 Some(element) => write!(f, "{}@e{}", self.partition, element.0),
93 None => write!(f, "{}@current", self.partition),
94 }
95 }
96}
97
98impl From<&str> for PartitionId {
99 fn from(value: &str) -> Self {
100 Self::new(value)
101 }
102}
103
104impl From<String> for PartitionId {
105 fn from(value: String) -> Self {
106 Self::new(value)
107 }
108}
109
110#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
111pub struct FederatedCut {
112 pub cuts: Vec<PartitionCut>,
113}
114
115impl FederatedCut {
116 pub fn normalized(mut self) -> Self {
117 self.cuts
118 .sort_by(|left, right| left.partition.cmp(&right.partition));
119 self
120 }
121}
122
123pub fn merge_partition_cuts<'a, I>(cuts: I) -> Vec<PartitionCut>
124where
125 I: IntoIterator<Item = &'a PartitionCut>,
126{
127 let mut merged = cuts.into_iter().cloned().collect::<Vec<_>>();
128 merged.sort_by(|left, right| {
129 left.partition
130 .cmp(&right.partition)
131 .then_with(|| left.as_of.cmp(&right.as_of))
132 });
133 merged.dedup();
134 merged
135}
136
137#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
138pub enum Value {
139 #[default]
140 Null,
141 Bool(bool),
142 I64(i64),
143 U64(u64),
144 F64(f64),
145 String(String),
146 Bytes(Vec<u8>),
147 Entity(EntityId),
148 List(Vec<Value>),
149}
150
151#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
152pub enum OperationKind {
153 #[default]
154 Assert,
155 Retract,
156 Add,
157 Remove,
158 InsertAfter,
159 LeaseOpen,
160 LeaseRenew,
161 LeaseExpire,
162 Claim,
163 Release,
164 Annotate,
165}
166
167#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
168pub struct CausalContext {
169 pub frontier: Vec<ElementId>,
170}
171
172#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
173pub struct SourceRef {
174 pub uri: String,
175 pub digest: Option<String>,
176}
177
178#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
179#[serde(rename_all = "snake_case")]
180pub enum SidecarKind {
181 #[default]
182 Artifact,
183 Vector,
184}
185
186#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
187pub struct SidecarOrigin {
188 pub kind: SidecarKind,
189 pub sidecar_id: String,
190 pub record_id: String,
191}
192
193#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
194pub struct DatomProvenance {
195 pub author_principal: String,
196 pub agent_id: String,
197 pub tool_id: String,
198 pub session_id: String,
199 pub source_ref: SourceRef,
200 pub parent_datom_ids: Vec<ElementId>,
201 pub confidence: f32,
202 pub trust_domain: String,
203 pub schema_version: String,
204}
205
206impl Default for DatomProvenance {
207 fn default() -> Self {
208 Self {
209 author_principal: String::new(),
210 agent_id: String::new(),
211 tool_id: String::new(),
212 session_id: String::new(),
213 source_ref: SourceRef::default(),
214 parent_datom_ids: Vec::new(),
215 confidence: 1.0,
216 trust_domain: String::new(),
217 schema_version: String::new(),
218 }
219 }
220}
221
222#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
223pub struct PolicyEnvelope {
224 pub capabilities: Vec<String>,
225 pub visibilities: Vec<String>,
226}
227
228#[derive(Clone, Debug, Default, Deserialize)]
229struct RawPolicyEnvelope {
230 #[serde(default)]
231 capabilities: Vec<String>,
232 #[serde(default)]
233 visibilities: Vec<String>,
234 #[serde(default)]
235 capability: Option<String>,
236 #[serde(default)]
237 visibility: Option<String>,
238}
239
240impl<'de> Deserialize<'de> for PolicyEnvelope {
241 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
242 where
243 D: Deserializer<'de>,
244 {
245 let raw = RawPolicyEnvelope::deserialize(deserializer)?;
246 let mut capabilities = raw.capabilities;
247 if let Some(capability) = raw.capability {
248 capabilities.push(capability);
249 }
250 let mut visibilities = raw.visibilities;
251 if let Some(visibility) = raw.visibility {
252 visibilities.push(visibility);
253 }
254 Ok(Self {
255 capabilities: normalize_policy_values(capabilities),
256 visibilities: normalize_policy_values(visibilities),
257 })
258 }
259}
260
261impl PolicyEnvelope {
262 pub fn is_public(&self) -> bool {
263 self.capabilities.is_empty() && self.visibilities.is_empty()
264 }
265
266 pub fn normalized(mut self) -> Self {
267 self.capabilities = normalize_policy_values(self.capabilities);
268 self.visibilities = normalize_policy_values(self.visibilities);
269 self
270 }
271
272 pub fn union_with(&mut self, other: &Self) {
273 self.capabilities.extend(other.capabilities.iter().cloned());
274 self.visibilities.extend(other.visibilities.iter().cloned());
275 self.capabilities = normalize_policy_values(std::mem::take(&mut self.capabilities));
276 self.visibilities = normalize_policy_values(std::mem::take(&mut self.visibilities));
277 }
278}
279
280pub fn merge_policy_envelopes<'a, I>(envelopes: I) -> Option<PolicyEnvelope>
281where
282 I: IntoIterator<Item = Option<&'a PolicyEnvelope>>,
283{
284 let mut merged = PolicyEnvelope::default();
285 for envelope in envelopes.into_iter().flatten() {
286 merged.union_with(envelope);
287 }
288 (!merged.is_public()).then_some(merged)
289}
290
291#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
292pub struct PolicyContext {
293 pub capabilities: Vec<String>,
294 pub visibilities: Vec<String>,
295}
296
297impl PolicyContext {
298 pub fn is_empty(&self) -> bool {
299 self.capabilities.is_empty() && self.visibilities.is_empty()
300 }
301
302 pub fn allows(&self, envelope: &PolicyEnvelope) -> bool {
303 let capability_allowed = envelope
304 .capabilities
305 .iter()
306 .all(|capability| self.capabilities.iter().any(|value| value == capability));
307 let visibility_allowed = envelope
308 .visibilities
309 .iter()
310 .all(|visibility| self.visibilities.iter().any(|value| value == visibility));
311 capability_allowed && visibility_allowed
312 }
313
314 pub fn subset_of(&self, other: &Self) -> bool {
315 self.capabilities
316 .iter()
317 .all(|value| other.capabilities.iter().any(|allowed| allowed == value))
318 && self
319 .visibilities
320 .iter()
321 .all(|value| other.visibilities.iter().any(|allowed| allowed == value))
322 }
323}
324
325pub fn policy_allows(context: Option<&PolicyContext>, envelope: Option<&PolicyEnvelope>) -> bool {
326 match envelope {
327 None => true,
328 Some(envelope) => match context {
329 Some(context) => context.allows(envelope),
330 None => envelope.is_public(),
331 },
332 }
333}
334
335fn normalize_policy_values(mut values: Vec<String>) -> Vec<String> {
336 values.sort_unstable();
337 values.dedup();
338 values
339}
340
341#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
342pub struct Datom {
343 pub entity: EntityId,
344 pub attribute: AttributeId,
345 pub value: Value,
346 pub op: OperationKind,
347 pub element: ElementId,
348 pub replica: ReplicaId,
349 pub causal_context: CausalContext,
350 pub provenance: DatomProvenance,
351 pub policy: Option<PolicyEnvelope>,
352}
353
354#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
355pub struct FactProvenance {
356 pub source_datom_ids: Vec<ElementId>,
357 #[serde(default, skip_serializing_if = "Vec::is_empty")]
358 pub imported_cuts: Vec<PartitionCut>,
359 pub sidecar_origin: Option<SidecarOrigin>,
360 pub source_ref: Option<SourceRef>,
361}
362
363#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
364pub struct Variable(pub String);
365
366impl Variable {
367 pub fn new(name: impl Into<String>) -> Self {
368 Self(name.into())
369 }
370}
371
372#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
373pub struct PredicateRef {
374 pub id: PredicateId,
375 pub name: String,
376 pub arity: usize,
377}
378
379#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
380pub enum AggregateFunction {
381 Count,
382 Sum,
383 Min,
384 Max,
385}
386
387impl fmt::Display for AggregateFunction {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 let label = match self {
390 Self::Count => "count",
391 Self::Sum => "sum",
392 Self::Min => "min",
393 Self::Max => "max",
394 };
395 write!(f, "{label}")
396 }
397}
398
399#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
400pub struct AggregateTerm {
401 pub function: AggregateFunction,
402 pub variable: Variable,
403}
404
405#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
406pub enum Term {
407 Variable(Variable),
408 Value(Value),
409 Aggregate(AggregateTerm),
410}
411
412#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
413pub struct Atom {
414 pub predicate: PredicateRef,
415 pub terms: Vec<Term>,
416}
417
418#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
419pub enum Literal {
420 Positive(Atom),
421 Negative(Atom),
422}
423
424#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
425pub struct RuleAst {
426 pub id: RuleId,
427 pub head: Atom,
428 pub body: Vec<Literal>,
429}
430
431#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
432pub struct QueryAst {
433 pub goals: Vec<Atom>,
434 pub keep: Vec<Variable>,
435}
436
437#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
438pub struct ExtensionalFact {
439 pub predicate: PredicateRef,
440 pub values: Vec<Value>,
441 pub policy: Option<PolicyEnvelope>,
442 pub provenance: Option<FactProvenance>,
443}
444
445#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
446pub enum TemporalView {
447 #[default]
448 Current,
449 AsOf(ElementId),
450}
451
452#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
453pub struct QuerySpec {
454 pub view: TemporalView,
455 pub query: QueryAst,
456}
457
458#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
459pub struct NamedQuerySpec {
460 pub name: Option<String>,
461 pub spec: QuerySpec,
462}
463
464#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
465pub enum ExplainTarget {
466 #[default]
467 Plan,
468 Tuple(Atom),
469}
470
471#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
472pub struct ExplainSpec {
473 pub view: TemporalView,
474 pub target: ExplainTarget,
475}
476
477#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
478pub struct NamedExplainSpec {
479 pub name: Option<String>,
480 pub spec: ExplainSpec,
481}
482
483#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
484pub struct RuleProgram {
485 pub predicates: Vec<PredicateRef>,
486 pub rules: Vec<RuleAst>,
487 pub materialized: Vec<PredicateId>,
488 pub facts: Vec<ExtensionalFact>,
489}
490
491#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
492pub struct Tuple {
493 pub id: TupleId,
494 pub predicate: PredicateId,
495 pub values: Vec<Value>,
496}
497
498#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
499pub struct DerivedTupleMetadata {
500 pub rule_id: RuleId,
501 pub predicate_id: PredicateId,
502 pub stratum: usize,
503 pub scc_id: usize,
504 pub iteration: usize,
505 pub parent_tuple_ids: Vec<TupleId>,
506 pub source_datom_ids: Vec<ElementId>,
507 #[serde(default, skip_serializing_if = "Vec::is_empty")]
508 pub imported_cuts: Vec<PartitionCut>,
509}
510
511#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
512pub struct DerivedTuple {
513 pub tuple: Tuple,
514 pub metadata: DerivedTupleMetadata,
515 pub policy: Option<PolicyEnvelope>,
516}
517
518#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
519pub struct QueryRow {
520 pub values: Vec<Value>,
521 pub tuple_id: Option<TupleId>,
522}
523
524#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
525pub struct QueryResult {
526 pub rows: Vec<QueryRow>,
527}
528
529#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
530pub struct DerivationTrace {
531 pub root: TupleId,
532 pub tuples: Vec<DerivedTuple>,
533}
534
535#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
536pub struct PhaseSignature {
537 pub available: Vec<String>,
538 pub provides: Vec<String>,
539 pub keep: Vec<String>,
540}
541
542#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
543pub struct PhaseNode {
544 pub id: String,
545 pub signature: PhaseSignature,
546 pub recursive_scc: Option<usize>,
547}
548
549#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
550pub struct PhaseEdge {
551 pub from: String,
552 pub to: String,
553}
554
555#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
556pub struct PhaseGraph {
557 pub nodes: Vec<PhaseNode>,
558 pub edges: Vec<PhaseEdge>,
559}
560
561#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
562pub struct PlanExplanation {
563 pub summary: String,
564 pub phase_graph: PhaseGraph,
565}
566
567#[cfg(test)]
568mod tests {
569 use super::{
570 merge_policy_envelopes, policy_allows, AttributeId, Datom, DatomProvenance, ElementId,
571 EntityId, FactProvenance, FederatedCut, OperationKind, PartitionCut, PartitionId,
572 PolicyContext, PolicyEnvelope, ReplicaId, SidecarKind, SidecarOrigin, SourceRef, Value,
573 };
574
575 #[test]
576 fn ids_are_deterministic_and_displayable() {
577 let entity = EntityId::new(7);
578 let attribute = AttributeId::new(11);
579 let element = ElementId::new(19);
580 let partition = PartitionId::new("ops-west");
581
582 assert_eq!(entity, EntityId::new(7));
583 assert_eq!(attribute.to_string(), "11");
584 assert_eq!(element.to_string(), "19");
585 assert_eq!(partition.to_string(), "ops-west");
586 }
587
588 #[test]
589 fn partition_cuts_format_and_normalize() {
590 let current = PartitionCut::current("tenant-b");
591 let as_of = PartitionCut::as_of("tenant-a", ElementId::new(7));
592
593 assert!(current.is_current());
594 assert_eq!(current.to_string(), "tenant-b@current");
595 assert_eq!(as_of.to_string(), "tenant-a@e7");
596
597 let normalized = FederatedCut {
598 cuts: vec![current.clone(), as_of.clone()],
599 }
600 .normalized();
601 assert_eq!(
602 normalized.cuts,
603 vec![
604 PartitionCut::as_of("tenant-a", ElementId::new(7)),
605 PartitionCut::current("tenant-b"),
606 ]
607 );
608 }
609
610 #[test]
611 fn value_default_is_null_and_round_trips_through_serde() {
612 assert_eq!(Value::default(), Value::Null);
613
614 let value = Value::List(vec![
615 Value::String("task-1".into()),
616 Value::Bool(true),
617 Value::Entity(EntityId::new(42)),
618 ]);
619
620 let json = serde_json::to_string(&value).expect("serialize value");
621 let decoded: Value = serde_json::from_str(&json).expect("deserialize value");
622
623 assert_eq!(decoded, value);
624 }
625
626 #[test]
627 fn provenance_defaults_and_datom_round_trip_preserve_fields() {
628 let provenance = DatomProvenance {
629 author_principal: "jamie".into(),
630 agent_id: "codex".into(),
631 tool_id: "shell".into(),
632 session_id: "session-1".into(),
633 source_ref: SourceRef {
634 uri: "file:///fixture".into(),
635 digest: Some("sha256:abc".into()),
636 },
637 parent_datom_ids: vec![ElementId::new(2), ElementId::new(3)],
638 confidence: 0.75,
639 trust_domain: "dev".into(),
640 schema_version: "v1".into(),
641 };
642 let datom = Datom {
643 entity: EntityId::new(1),
644 attribute: AttributeId::new(2),
645 value: Value::String("ready".into()),
646 op: OperationKind::Assert,
647 element: ElementId::new(4),
648 replica: ReplicaId::new(5),
649 causal_context: Default::default(),
650 provenance: provenance.clone(),
651 policy: None,
652 };
653
654 assert_eq!(DatomProvenance::default().confidence, 1.0);
655
656 let json = serde_json::to_string(&datom).expect("serialize datom");
657 let decoded: Datom = serde_json::from_str(&json).expect("deserialize datom");
658
659 assert_eq!(decoded.provenance, provenance);
660 assert_eq!(decoded, datom);
661 }
662
663 #[test]
664 fn fact_provenance_round_trips_with_sidecar_origin() {
665 let provenance = FactProvenance {
666 source_datom_ids: vec![ElementId::new(7)],
667 imported_cuts: Vec::new(),
668 sidecar_origin: Some(SidecarOrigin {
669 kind: SidecarKind::Vector,
670 sidecar_id: "vector-sidecar".into(),
671 record_id: "vec-1".into(),
672 }),
673 source_ref: Some(SourceRef {
674 uri: "s3://vectors/vec-1".into(),
675 digest: Some("sha256:def".into()),
676 }),
677 };
678
679 let json = serde_json::to_string(&provenance).expect("serialize fact provenance");
680 let decoded: FactProvenance =
681 serde_json::from_str(&json).expect("deserialize fact provenance");
682
683 assert_eq!(decoded, provenance);
684 }
685
686 #[test]
687 fn policy_context_requires_capability_and_visibility_when_present() {
688 let envelope = PolicyEnvelope {
689 capabilities: vec!["executor".into()],
690 visibilities: vec!["ops".into()],
691 };
692 let allowed = PolicyContext {
693 capabilities: vec!["executor".into()],
694 visibilities: vec!["ops".into()],
695 };
696 let missing_visibility = PolicyContext {
697 capabilities: vec!["executor".into()],
698 visibilities: vec![],
699 };
700
701 assert!(policy_allows(Some(&allowed), Some(&envelope)));
702 assert!(!policy_allows(Some(&missing_visibility), Some(&envelope)));
703 assert!(!policy_allows(None, Some(&envelope)));
704 assert!(policy_allows(None, None));
705 }
706
707 #[test]
708 fn policy_envelope_deserializes_legacy_single_value_fields() {
709 let decoded: PolicyEnvelope =
710 serde_json::from_str(r#"{"capability":"executor","visibility":"ops"}"#)
711 .expect("deserialize legacy policy envelope");
712
713 assert_eq!(
714 decoded,
715 PolicyEnvelope {
716 capabilities: vec!["executor".into()],
717 visibilities: vec!["ops".into()],
718 }
719 );
720 }
721
722 #[test]
723 fn policy_envelope_merge_is_conjunctive_union() {
724 let merged = merge_policy_envelopes([
725 Some(&PolicyEnvelope {
726 capabilities: vec!["executor".into()],
727 visibilities: vec!["ops".into()],
728 }),
729 Some(&PolicyEnvelope {
730 capabilities: vec!["memory_reader".into()],
731 visibilities: vec!["finance".into()],
732 }),
733 ])
734 .expect("merged policy envelope");
735
736 assert_eq!(
737 merged,
738 PolicyEnvelope {
739 capabilities: vec!["executor".into(), "memory_reader".into()],
740 visibilities: vec!["finance".into(), "ops".into()],
741 }
742 );
743 }
744
745 #[test]
746 fn policy_context_subset_checks_requested_capabilities_and_visibilities() {
747 let granted = PolicyContext {
748 capabilities: vec!["executor".into(), "operator".into()],
749 visibilities: vec!["ops".into(), "finance".into()],
750 };
751 let requested = PolicyContext {
752 capabilities: vec!["executor".into()],
753 visibilities: vec!["ops".into()],
754 };
755 let escalated = PolicyContext {
756 capabilities: vec!["admin".into()],
757 visibilities: vec!["ops".into()],
758 };
759
760 assert!(requested.subset_of(&granted));
761 assert!(!escalated.subset_of(&granted));
762 assert!(PolicyContext::default().is_empty());
763 assert!(!granted.is_empty());
764 }
765}