bcx_model/event.rs
1use bcx_core::{CapabilityRef, EventId, PolicyEpoch, ValidationError};
2use bcx_wire::WireLimits;
3
4/// Relationship between a BCX event and one of its parents.
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum RelationshipKind {
7 /// The parent directly caused this event.
8 CausedBy,
9 /// The event was delegated by the parent.
10 DelegatedFrom,
11 /// The event retries the parent.
12 RetryOf,
13 /// The event was scheduled by the parent.
14 ScheduledBy,
15 /// The event was derived from the parent.
16 DerivedFrom,
17 /// The event joins several parent branches.
18 JoinedFrom,
19}
20
21/// Observable cause class for an operation.
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum CauseKind {
24 /// An external network request entered the local trust boundary.
25 ExternalRequest,
26 /// A runtime or client attested an explicit user action.
27 ExplicitUserAction,
28 /// Application code initiated the operation.
29 ApplicationAction,
30 /// A service call was delegated by another participant.
31 DelegatedServiceCall,
32 /// A timer or schedule initiated the operation.
33 Timer,
34 /// A queue message initiated the operation.
35 QueueMessage,
36 /// The operation is a retry.
37 Retry,
38 /// An administrator initiated the operation.
39 Administrator,
40 /// An autonomous agent initiated the operation.
41 AutonomousAgent,
42}
43
44/// High-level operation action.
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum OperationAction {
47 /// Read data without modifying authoritative state.
48 Read,
49 /// Create a new object or state transition.
50 Create,
51 /// Update existing state.
52 Update,
53 /// Delete or tombstone existing state.
54 Delete,
55 /// Derive an output from one or more inputs.
56 Derive,
57 /// Execute a component or tool.
58 Execute,
59 /// Transfer data or authority across a boundary.
60 Transfer,
61 /// Subscribe to future updates.
62 Subscribe,
63 /// Publish an event.
64 Publish,
65}
66
67/// Admission decision produced before execution.
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub enum AdmissionResult {
70 /// The operation may continue as requested.
71 Allow,
72 /// The operation is denied.
73 Deny,
74 /// The operation may continue only with a narrower scope.
75 Narrow,
76 /// The operation requires stronger approval.
77 RequireApproval,
78 /// The operation is quarantined for later review.
79 Quarantine,
80}
81
82/// Execution result recorded after an operation attempt.
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum EffectResult {
85 /// The operation completed.
86 Completed,
87 /// The operation partially completed.
88 Partial,
89 /// The executor rejected the operation.
90 Rejected,
91 /// Execution failed.
92 Failed,
93 /// Execution was cancelled.
94 Cancelled,
95 /// Execution timed out.
96 TimedOut,
97}
98
99/// Compact event capsule for causal parentage.
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
101pub struct CauseCapsuleParts<'a> {
102 /// Local event identifier.
103 pub event_id: EventId,
104 /// Parent event identifiers.
105 ///
106 /// The compact capsule uses the same [`RelationshipKind`] for every
107 /// parent. Use separate capsules when parents need different relationship
108 /// meanings.
109 pub parents: &'a [EventId],
110 /// Relationship used for every parent in this compact capsule.
111 pub relationship: RelationshipKind,
112 /// Observable cause class.
113 pub cause_kind: CauseKind,
114 /// Requested action.
115 pub action: OperationAction,
116 /// Optional authority reference.
117 pub authority: Option<CapabilityRef>,
118 /// Optional policy epoch reference.
119 pub policy_epoch: Option<PolicyEpoch>,
120}
121
122/// Validated compact event capsule for causal parentage.
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124pub struct CauseCapsule<'a> {
125 event_id: EventId,
126 parents: &'a [EventId],
127 relationship: RelationshipKind,
128 cause_kind: CauseKind,
129 action: OperationAction,
130 authority: Option<CapabilityRef>,
131 policy_epoch: Option<PolicyEpoch>,
132}
133
134impl<'a> CauseCapsule<'a> {
135 /// Creates a validated compact cause capsule.
136 pub fn new(parts: CauseCapsuleParts<'a>, limits: WireLimits) -> Result<Self, ValidationError> {
137 let capsule = Self {
138 event_id: parts.event_id,
139 parents: parts.parents,
140 relationship: parts.relationship,
141 cause_kind: parts.cause_kind,
142 action: parts.action,
143 authority: parts.authority,
144 policy_epoch: parts.policy_epoch,
145 };
146 match capsule.validate(limits) {
147 Ok(()) => Ok(capsule),
148 Err(error) => Err(error),
149 }
150 }
151
152 /// Validates bounded capsule shape and rejects direct self-parent cycles.
153 pub fn validate(&self, limits: WireLimits) -> Result<(), ValidationError> {
154 if self.parents.is_empty() {
155 return Err(ValidationError::Empty);
156 }
157 if self.parents.len() > limits.maximum_parent_events() {
158 return Err(ValidationError::TooLarge);
159 }
160 if self.parents.iter().any(|parent| parent == &self.event_id) {
161 return Err(ValidationError::Malformed);
162 }
163 Ok(())
164 }
165
166 /// Returns the local event identifier.
167 #[must_use]
168 pub const fn event_id(&self) -> EventId {
169 self.event_id
170 }
171
172 /// Returns parent event identifiers.
173 #[must_use]
174 pub const fn parents(&self) -> &'a [EventId] {
175 self.parents
176 }
177
178 /// Returns the relationship used for each parent.
179 #[must_use]
180 pub const fn relationship(&self) -> RelationshipKind {
181 self.relationship
182 }
183
184 /// Returns the observable cause class.
185 #[must_use]
186 pub const fn cause_kind(&self) -> CauseKind {
187 self.cause_kind
188 }
189
190 /// Returns the requested action.
191 #[must_use]
192 pub const fn action(&self) -> OperationAction {
193 self.action
194 }
195
196 /// Returns the optional authority reference.
197 #[must_use]
198 pub const fn authority(&self) -> Option<CapabilityRef> {
199 self.authority
200 }
201
202 /// Returns the optional policy epoch reference.
203 #[must_use]
204 pub const fn policy_epoch(&self) -> Option<PolicyEpoch> {
205 self.policy_epoch
206 }
207}