1use alloc::boxed::Box;
2
3use crate::algebra::{ResourceVector, WideResourceVector};
4use crate::wire::{BindingEpoch, ConversationId, DeliverySeq, ParticipantId, ParticipantIndex};
5
6use super::{
7 ActiveBinding, CommittedDiedTerminal, ObserverProgressProjection,
8 claim_frontier::{ValidatedMarkerCandidate, ValidatedMarkerRecord},
9 storage::{
10 DetachedCredentialRecoveryRestore, DetachedMarkerReleaseProvenanceRestore,
11 DetachedMarkerReleaseRestore, MarkerCursorProgressRestore, MarkerDeliveryRestore,
12 OrdinaryBindingAuthorityRestore, RestoredBindingFateTerminal, StoredEdgeRestore,
13 },
14};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct ClosureDebt(WideResourceVector);
19
20impl ClosureDebt {
21 #[must_use]
23 pub const fn new(value: WideResourceVector) -> Option<Self> {
24 if value.is_zero() {
25 None
26 } else {
27 Some(Self(value))
28 }
29 }
30
31 #[must_use]
33 pub const fn value(self) -> WideResourceVector {
34 self.0
35 }
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct ObserverProjection {
41 through_seq: DeliverySeq,
42}
43
44impl ObserverProjection {
45 #[must_use]
47 pub const fn new(through_seq: DeliverySeq) -> Self {
48 Self { through_seq }
49 }
50
51 #[must_use]
53 pub const fn through_seq(self) -> DeliverySeq {
54 self.through_seq
55 }
56
57 #[must_use]
59 pub const fn storage_description(self) -> StoredEdgeRestore {
60 StoredEdgeRestore::ObserverProjection {
61 through_seq: self.through_seq,
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub struct PhysicalCompaction {
69 from_floor: DeliverySeq,
70 through_seq: DeliverySeq,
71}
72
73impl PhysicalCompaction {
74 #[must_use]
76 pub const fn new(from_floor: DeliverySeq, through_seq: DeliverySeq) -> Option<Self> {
77 if from_floor <= through_seq {
78 Some(Self {
79 from_floor,
80 through_seq,
81 })
82 } else {
83 None
84 }
85 }
86
87 #[must_use]
89 pub const fn from_floor(self) -> DeliverySeq {
90 self.from_floor
91 }
92
93 #[must_use]
95 pub const fn through_seq(self) -> DeliverySeq {
96 self.through_seq
97 }
98
99 #[must_use]
101 pub const fn storage_description(self) -> StoredEdgeRestore {
102 StoredEdgeRestore::PhysicalCompaction {
103 from_floor: self.from_floor,
104 through_seq: self.through_seq,
105 }
106 }
107}
108
109#[allow(clippy::struct_field_names)]
130#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131pub struct MarkerDelivery {
132 conversation_id: ConversationId,
133 participant_id: ParticipantId,
134 binding_epoch: BindingEpoch,
135 marker_delivery_seq: DeliverySeq,
136}
137
138impl MarkerDelivery {
139 #[must_use]
147 pub(super) const fn successor_from_validated_candidate(
148 candidate: ValidatedMarkerCandidate,
149 ) -> StoredEdge {
150 let conversation_id = candidate.conversation_id();
151 let participant_id = candidate.participant_id();
152 let marker_delivery_seq = candidate.delivery_seq();
153 let successor = match candidate.target_binding() {
154 super::FrontierBinding::Bound(binding_epoch) => StoredEdge::MarkerDelivery(Self {
155 conversation_id,
156 participant_id,
157 binding_epoch,
158 marker_delivery_seq,
159 }),
160 super::FrontierBinding::Detached(last_dead_binding_epoch) => {
161 StoredEdge::DetachedMarkerRelease(DetachedMarkerRelease {
162 conversation_id,
163 participant_id,
164 marker_delivery_seq,
165 last_dead_binding_epoch,
166 provenance: DetachedMarkerReleaseProvenance::Candidate,
167 })
168 }
169 };
170 candidate.consume();
171 successor
172 }
173
174 #[must_use]
176 pub(super) const fn from_validated_record(record: &ValidatedMarkerRecord) -> Self {
177 Self {
178 conversation_id: record.conversation_id(),
179 participant_id: record.participant_id(),
180 binding_epoch: record.binding_epoch(),
181 marker_delivery_seq: record.delivery_seq(),
182 }
183 }
184
185 #[must_use]
187 pub const fn conversation_id(self) -> ConversationId {
188 self.conversation_id
189 }
190
191 #[must_use]
193 pub const fn participant_id(self) -> ParticipantId {
194 self.participant_id
195 }
196
197 #[must_use]
199 pub const fn binding_epoch(self) -> BindingEpoch {
200 self.binding_epoch
201 }
202
203 #[must_use]
205 pub const fn marker_delivery_seq(self) -> DeliverySeq {
206 self.marker_delivery_seq
207 }
208
209 #[must_use]
216 pub const fn storage_description(self) -> StoredEdgeRestore {
217 StoredEdgeRestore::MarkerDelivery(MarkerDeliveryRestore {
218 participant_id: self.participant_id,
219 binding_epoch: self.binding_epoch,
220 marker_delivery_seq: self.marker_delivery_seq,
221 })
222 }
223}
224
225#[cfg(any(test, feature = "test-support"))]
226#[allow(clippy::expect_used, clippy::too_many_lines)]
227pub fn validated_marker_record_for_test(
228 conversation_id: crate::wire::ConversationId,
229 participant_id: ParticipantId,
230 target_binding: super::claim_frontier::FrontierBinding,
231 marker_delivery_seq: DeliverySeq,
232 cursor: DeliverySeq,
233) -> ValidatedMarkerRecord {
234 use alloc::{vec, vec::Vec};
235
236 use crate::outcome::CandidatePhase;
237
238 use super::{
239 AdmissionOrder, OrderClaims, OrderHigh, OrderLedger, RecoverySequenceReserve,
240 SequenceClaims, SequenceLedger,
241 claim_frontier::{
242 BindingTerminalOwner, ClaimFrontiers, ClaimFrontiersRestore, FrontierBinding,
243 FrontierParticipant, ImmutableSequenceCandidate, MarkerProvenance, MarkerRecordRequest,
244 MovableOrderClaim, MovableSequenceClaim, OrderClaimFrontierRestore, OrderDirectOwner,
245 RetainedCausalRecord, RetainedCausalRecordKind, SequenceClaimFrontierRestore,
246 SequenceDirectOwner, SequenceProductRangesRestore, TerminalProductRangeRestore,
247 },
248 };
249
250 let identity_slot_limit = participant_id
251 .checked_add(1)
252 .expect("test participant must fit a half-open identity domain");
253 let exit_seq = marker_delivery_seq
254 .checked_add(1)
255 .expect("test marker must leave an exit-claim suffix");
256 let binding_epoch = match target_binding {
257 FrontierBinding::Bound(epoch) | FrontierBinding::Detached(epoch) => epoch,
258 };
259 let terminal_owner = BindingTerminalOwner {
260 participant_index: participant_id,
261 binding_epoch,
262 };
263 let (sequence_claims, sequence_movable, products, order_claims, order_movable) =
264 match target_binding {
265 FrontierBinding::Bound(_) => {
266 let terminal_seq = exit_seq
267 .checked_add(1)
268 .expect("test marker must leave a terminal-claim suffix");
269 let product_seq = terminal_seq
270 .checked_add(1)
271 .expect("test marker must leave a terminal-product suffix");
272 (
273 SequenceClaims::new(1, 1, 0, RecoverySequenceReserve::None),
274 vec![
275 MovableSequenceClaim {
276 delivery_seq: exit_seq,
277 owner: SequenceDirectOwner::MembershipExit {
278 participant_index: participant_id,
279 },
280 },
281 MovableSequenceClaim {
282 delivery_seq: terminal_seq,
283 owner: SequenceDirectOwner::BindingTerminal(terminal_owner),
284 },
285 ],
286 SequenceProductRangesRestore {
287 live_times_terminal: vec![TerminalProductRangeRestore {
288 start: product_seq,
289 length: 1,
290 terminal: terminal_owner,
291 }],
292 live_times_replacement_terminal: None,
293 other_live_times_exit: vec![],
294 },
295 OrderClaims::new(1, 1, false, false)
296 .expect("bound test claims have no torn recovery pair"),
297 vec![
298 MovableOrderClaim {
299 transaction_order: 1,
300 owner: OrderDirectOwner::ActiveBindingTerminal(terminal_owner),
301 },
302 MovableOrderClaim {
303 transaction_order: 2,
304 owner: OrderDirectOwner::MembershipExit {
305 participant_index: participant_id,
306 },
307 },
308 ],
309 )
310 }
311 FrontierBinding::Detached(_) => (
312 SequenceClaims::new(1, 0, 0, RecoverySequenceReserve::None),
313 vec![MovableSequenceClaim {
314 delivery_seq: exit_seq,
315 owner: SequenceDirectOwner::MembershipExit {
316 participant_index: participant_id,
317 },
318 }],
319 SequenceProductRangesRestore::default(),
320 OrderClaims::new(0, 1, false, false)
321 .expect("detached test claims have no torn recovery pair"),
322 vec![MovableOrderClaim {
323 transaction_order: 1,
324 owner: OrderDirectOwner::MembershipExit {
325 participant_index: participant_id,
326 },
327 }],
328 ),
329 };
330 let sequence_ledger = SequenceLedger::try_new(marker_delivery_seq, sequence_claims)
331 .expect("test sequence frontier is within the numeric suffix");
332 let order_ledger = OrderLedger::try_new(OrderHigh::Allocated(0), order_claims)
333 .expect("test order frontier is within the numeric suffix");
334 let admission_order = AdmissionOrder::new(0, CandidatePhase::CompactionMarker, participant_id);
335 let mut prevalidated = ClaimFrontiers::prevalidate(
336 ClaimFrontiersRestore {
337 conversation_id,
338 active_identities: vec![FrontierParticipant::new(
339 participant_id,
340 cursor,
341 target_binding,
342 )],
343 identity_slot_limit,
344 retained_floor: u128::from(marker_delivery_seq),
345 retained_record_limit: 1,
346 retained_records: vec![RetainedCausalRecord {
347 delivery_seq: marker_delivery_seq,
348 admission_order,
349 kind: RetainedCausalRecordKind::CompactionMarker {
350 participant_index: participant_id,
351 provenance: MarkerProvenance::NonProductM,
352 },
353 }],
354 active_marker_anchors: vec![marker_delivery_seq],
355 historical_marker_deliveries: vec![],
356 historical_causal_facts: vec![],
357 sequence: SequenceClaimFrontierRestore {
358 movable_claims: sequence_movable,
359 immutable_candidates: Vec::<ImmutableSequenceCandidate>::new(),
360 products,
361 recovery: None,
362 },
363 order: OrderClaimFrontierRestore {
364 movable_claims: order_movable,
365 immutable_candidates: vec![],
366 recovery: None,
367 },
368 recovery_marker_delivery_seq: None,
369 },
370 sequence_ledger,
371 order_ledger,
372 )
373 .expect("complete test claim frontier must prevalidate");
374 let record = prevalidated
375 .take_marker_record(MarkerRecordRequest::planned(
376 participant_id,
377 marker_delivery_seq,
378 target_binding,
379 ))
380 .expect("prevalidated test frontier retains its exact marker");
381 if cursor >= marker_delivery_seq {
382 record.delivered_for_test()
383 } else {
384 record
385 }
386}
387
388#[cfg(any(test, feature = "test-support"))]
389pub fn marker_delivery_for_test(
390 participant_id: ParticipantId,
391 binding_epoch: BindingEpoch,
392 marker_delivery_seq: DeliverySeq,
393) -> Result<MarkerDelivery, super::storage::StorageRestoreError> {
394 let record = validated_marker_record_for_test(
395 1,
396 participant_id,
397 super::claim_frontier::FrontierBinding::Bound(binding_epoch),
398 marker_delivery_seq,
399 marker_delivery_seq.saturating_sub(1),
400 );
401 super::storage::MarkerDeliveryRestore {
402 participant_id,
403 binding_epoch,
404 marker_delivery_seq,
405 }
406 .restore_bound(1, record)
407}
408
409#[derive(Clone, Copy, Debug, PartialEq, Eq)]
427pub struct CursorProgressContinuous {
428 participant_id: ParticipantId,
429 binding_epoch: BindingEpoch,
430 through_seq: DeliverySeq,
431}
432
433impl CursorProgressContinuous {
434 #[cfg(test)]
436 #[must_use]
437 pub(crate) const fn new(
438 participant_id: ParticipantId,
439 binding_epoch: BindingEpoch,
440 through_seq: DeliverySeq,
441 ) -> Self {
442 Self {
443 participant_id,
444 binding_epoch,
445 through_seq,
446 }
447 }
448
449 #[must_use]
451 pub const fn participant_id(self) -> ParticipantId {
452 self.participant_id
453 }
454
455 #[must_use]
457 pub const fn binding_epoch(self) -> BindingEpoch {
458 self.binding_epoch
459 }
460
461 #[must_use]
463 pub const fn through_seq(self) -> DeliverySeq {
464 self.through_seq
465 }
466
467 #[must_use]
478 pub const fn storage_description(self, conversation_id: ConversationId) -> StoredEdgeRestore {
479 StoredEdgeRestore::ParticipantCursorProgressContinuous {
480 participant_id: self.participant_id,
481 binding_epoch: self.binding_epoch,
482 through_seq: self.through_seq,
483 authority: OrdinaryBindingAuthorityRestore {
484 binding: ActiveBinding {
485 participant_id: self.participant_id,
486 conversation_id,
487 binding_epoch: self.binding_epoch,
488 },
489 through_seq: self.through_seq,
490 },
491 }
492 }
493}
494
495#[derive(Clone, Copy, Debug, PartialEq, Eq)]
502pub struct CursorProgressMarker {
503 conversation_id: ConversationId,
504 participant_id: ParticipantId,
505 binding_epoch: BindingEpoch,
506 through_seq: DeliverySeq,
507 marker_delivery_seq: DeliverySeq,
508}
509
510impl CursorProgressMarker {
511 #[must_use]
513 pub const fn conversation_id(self) -> ConversationId {
514 self.conversation_id
515 }
516
517 #[must_use]
519 pub const fn participant_id(self) -> ParticipantId {
520 self.participant_id
521 }
522
523 #[must_use]
525 pub const fn binding_epoch(self) -> BindingEpoch {
526 self.binding_epoch
527 }
528
529 #[must_use]
531 pub const fn through_seq(self) -> DeliverySeq {
532 self.through_seq
533 }
534
535 #[must_use]
537 pub const fn marker_delivery_seq(self) -> DeliverySeq {
538 self.marker_delivery_seq
539 }
540
541 #[must_use]
547 pub const fn storage_description(self) -> StoredEdgeRestore {
548 StoredEdgeRestore::ParticipantCursorProgressMarker(MarkerCursorProgressRestore {
549 conversation_id: self.conversation_id,
550 participant_id: self.participant_id,
551 binding_epoch: self.binding_epoch,
552 through_seq: self.through_seq,
553 marker_delivery_seq: self.marker_delivery_seq,
554 delivery: MarkerDeliveryRestore {
555 participant_id: self.participant_id,
556 binding_epoch: self.binding_epoch,
557 marker_delivery_seq: self.marker_delivery_seq,
558 },
559 })
560 }
561}
562
563#[derive(Clone, Copy, Debug, PartialEq, Eq)]
579pub enum ParticipantCursorProgress {
580 Continuous(CursorProgressContinuous),
582 Marker(CursorProgressMarker),
584}
585
586impl ParticipantCursorProgress {
587 #[cfg(test)]
589 #[must_use]
590 pub(crate) const fn continuous(
591 participant_id: ParticipantId,
592 binding_epoch: BindingEpoch,
593 through_seq: DeliverySeq,
594 ) -> Self {
595 Self::Continuous(CursorProgressContinuous::new(
596 participant_id,
597 binding_epoch,
598 through_seq,
599 ))
600 }
601
602 pub(super) fn restore_continuous(
603 authority: OrdinaryBindingAuthority,
604 participant_id: ParticipantId,
605 binding_epoch: BindingEpoch,
606 through_seq: DeliverySeq,
607 ) -> Option<Self> {
608 if authority.binding.participant_id != participant_id
609 || authority.binding.binding_epoch != binding_epoch
610 || authority.through_seq != through_seq
611 {
612 return None;
613 }
614 Some(Self::Continuous(CursorProgressContinuous {
615 participant_id,
616 binding_epoch,
617 through_seq,
618 }))
619 }
620
621 #[must_use]
623 pub const fn participant_id(self) -> ParticipantId {
624 match self {
625 Self::Continuous(value) => value.participant_id,
626 Self::Marker(value) => value.participant_id,
627 }
628 }
629
630 #[must_use]
632 pub const fn binding_epoch(self) -> BindingEpoch {
633 match self {
634 Self::Continuous(value) => value.binding_epoch,
635 Self::Marker(value) => value.binding_epoch,
636 }
637 }
638
639 #[must_use]
641 pub const fn through_seq(self) -> DeliverySeq {
642 match self {
643 Self::Continuous(value) => value.through_seq,
644 Self::Marker(value) => value.through_seq,
645 }
646 }
647
648 #[must_use]
650 pub const fn marker_delivery_seq(self) -> Option<DeliverySeq> {
651 match self {
652 Self::Continuous(_) => None,
653 Self::Marker(value) => Some(value.marker_delivery_seq),
654 }
655 }
656}
657
658#[derive(Clone, Copy, Debug, PartialEq, Eq)]
663pub struct DetachedCredentialRecovery {
664 conversation_id: ConversationId,
665 participant_id: ParticipantId,
666 marker_delivery_seq: DeliverySeq,
667 prior_binding_epoch: BindingEpoch,
668 resulting_floor: DeliverySeq,
676}
677
678impl DetachedCredentialRecovery {
679 #[must_use]
682 pub const fn resulting_floor(self) -> DeliverySeq {
683 self.resulting_floor
684 }
685
686 pub fn storage_description(
708 self,
709 terminal: RestoredBindingFateTerminal,
710 ) -> Result<StoredEdgeRestore, RestoredBindingFateTerminal> {
711 if terminal.participant_id() != self.participant_id
712 || terminal.binding_epoch() != self.prior_binding_epoch
713 || terminal.conversation_id() != self.conversation_id
714 {
715 return Err(terminal);
716 }
717 Ok(StoredEdgeRestore::DetachedCredentialRecovery(
718 DetachedCredentialRecoveryRestore {
719 participant_id: self.participant_id,
720 marker_delivery_seq: self.marker_delivery_seq,
721 prior_binding_epoch: self.prior_binding_epoch,
722 resulting_floor: self.resulting_floor,
723 terminal: terminal.storage_description(),
724 progress: MarkerCursorProgressRestore {
725 conversation_id: self.conversation_id,
726 participant_id: self.participant_id,
727 binding_epoch: self.prior_binding_epoch,
728 through_seq: self.marker_delivery_seq,
729 marker_delivery_seq: self.marker_delivery_seq,
730 delivery: MarkerDeliveryRestore {
731 participant_id: self.participant_id,
732 binding_epoch: self.prior_binding_epoch,
733 marker_delivery_seq: self.marker_delivery_seq,
734 },
735 },
736 },
737 ))
738 }
739
740 pub(super) const fn from_storage_description(
747 conversation_id: ConversationId,
748 participant_id: ParticipantId,
749 marker_delivery_seq: DeliverySeq,
750 prior_binding_epoch: BindingEpoch,
751 resulting_floor: DeliverySeq,
752 ) -> Self {
753 Self {
754 conversation_id,
755 participant_id,
756 marker_delivery_seq,
757 prior_binding_epoch,
758 resulting_floor,
759 }
760 }
761
762 #[must_use]
764 pub const fn conversation_id(self) -> ConversationId {
765 self.conversation_id
766 }
767
768 #[must_use]
770 pub const fn participant_id(self) -> ParticipantId {
771 self.participant_id
772 }
773
774 #[must_use]
776 pub const fn marker_delivery_seq(self) -> DeliverySeq {
777 self.marker_delivery_seq
778 }
779
780 #[must_use]
782 pub const fn prior_binding_epoch(self) -> BindingEpoch {
783 self.prior_binding_epoch
784 }
785}
786
787#[cfg(any(test, feature = "test-support"))]
788pub fn validated_marker_record_for_recovery_test(
789 recovery: DetachedCredentialRecovery,
790) -> ValidatedMarkerRecord {
791 validated_marker_record_for_test(
792 recovery.conversation_id(),
793 recovery.participant_id(),
794 super::claim_frontier::FrontierBinding::Detached(recovery.prior_binding_epoch()),
795 recovery.marker_delivery_seq(),
796 recovery.marker_delivery_seq(),
797 )
798}
799
800#[derive(Clone, Copy, Debug, PartialEq, Eq)]
805pub struct DetachedMarkerRelease {
806 conversation_id: ConversationId,
814 participant_id: ParticipantId,
815 marker_delivery_seq: DeliverySeq,
816 last_dead_binding_epoch: BindingEpoch,
817 provenance: DetachedMarkerReleaseProvenance,
825}
826
827#[derive(Clone, Copy, Debug, PartialEq, Eq)]
832pub enum DetachedMarkerReleaseProvenance {
833 Fate,
836 Candidate,
840}
841
842impl DetachedMarkerRelease {
843 #[must_use]
845 pub const fn conversation_id(self) -> ConversationId {
846 self.conversation_id
847 }
848
849 #[must_use]
851 pub const fn provenance(self) -> DetachedMarkerReleaseProvenance {
852 self.provenance
853 }
854
855 pub(super) const fn from_storage_description(
859 conversation_id: ConversationId,
860 participant_id: ParticipantId,
861 marker_delivery_seq: DeliverySeq,
862 last_dead_binding_epoch: BindingEpoch,
863 provenance: DetachedMarkerReleaseProvenance,
864 ) -> Self {
865 Self {
866 conversation_id,
867 participant_id,
868 marker_delivery_seq,
869 last_dead_binding_epoch,
870 provenance,
871 }
872 }
873
874 #[must_use]
876 pub const fn participant_id(self) -> ParticipantId {
877 self.participant_id
878 }
879
880 #[must_use]
882 pub const fn marker_delivery_seq(self) -> DeliverySeq {
883 self.marker_delivery_seq
884 }
885
886 #[must_use]
888 pub const fn last_dead_binding_epoch(self) -> BindingEpoch {
889 self.last_dead_binding_epoch
890 }
891
892 pub fn storage_description(
907 self,
908 terminal: Option<RestoredBindingFateTerminal>,
909 ) -> Result<StoredEdgeRestore, Option<RestoredBindingFateTerminal>> {
910 let provenance = match (self.provenance, terminal) {
911 (DetachedMarkerReleaseProvenance::Fate, Some(terminal)) => {
912 if terminal.participant_id() != self.participant_id
913 || terminal.binding_epoch() != self.last_dead_binding_epoch
914 || terminal.conversation_id() != self.conversation_id
915 {
916 return Err(Some(terminal));
917 }
918 DetachedMarkerReleaseProvenanceRestore::Fate {
919 terminal: terminal.storage_description(),
920 }
921 }
922 (DetachedMarkerReleaseProvenance::Candidate, None) => {
923 DetachedMarkerReleaseProvenanceRestore::Candidate
924 }
925 (DetachedMarkerReleaseProvenance::Fate, None)
926 | (DetachedMarkerReleaseProvenance::Candidate, Some(_)) => return Err(terminal),
927 };
928 Ok(StoredEdgeRestore::DetachedMarkerRelease(
929 DetachedMarkerReleaseRestore {
930 conversation_id: self.conversation_id,
931 participant_id: self.participant_id,
932 marker_delivery_seq: self.marker_delivery_seq,
933 last_dead_binding_epoch: self.last_dead_binding_epoch,
934 delivery: MarkerDeliveryRestore {
935 participant_id: self.participant_id,
936 binding_epoch: self.last_dead_binding_epoch,
937 marker_delivery_seq: self.marker_delivery_seq,
938 },
939 provenance,
940 },
941 ))
942 }
943}
944
945#[derive(Clone, Copy, Debug, PartialEq, Eq)]
950pub struct DetachedCursorRelease {
951 participant_id: ParticipantId,
952 last_dead_binding_epoch: BindingEpoch,
953}
954
955impl DetachedCursorRelease {
956 #[must_use]
958 pub const fn participant_id(self) -> ParticipantId {
959 self.participant_id
960 }
961
962 #[must_use]
964 pub const fn last_dead_binding_epoch(self) -> BindingEpoch {
965 self.last_dead_binding_epoch
966 }
967}
968
969#[derive(Clone, Copy, Debug, PartialEq, Eq)]
971pub enum StoredEdge {
972 ObserverProjection(ObserverProjection),
974 PhysicalCompaction(PhysicalCompaction),
976 MarkerDelivery(MarkerDelivery),
978 ParticipantCursorProgress(ParticipantCursorProgress),
980 DetachedCredentialRecovery(DetachedCredentialRecovery),
982 DetachedMarkerRelease(DetachedMarkerRelease),
984 DetachedCursorRelease(DetachedCursorRelease),
986}
987
988#[derive(Clone, Copy, Debug, PartialEq, Eq)]
990pub enum ClosureState {
991 Clear,
993 Owed {
995 debt: ClosureDebt,
997 edge: StoredEdge,
999 },
1000}
1001
1002#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1008pub struct OrdinaryDetachedAttachAdmission {
1009 _private: (),
1010}
1011
1012impl ClosureState {
1013 pub const fn ordinary_detached_attach_admission(
1020 self,
1021 ) -> Result<OrdinaryDetachedAttachAdmission, Self> {
1022 match self {
1023 Self::Clear => Ok(OrdinaryDetachedAttachAdmission { _private: () }),
1024 Self::Owed { .. } => Err(self),
1025 }
1026 }
1027}
1028
1029#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1035pub struct DebtCompletion(ClosureState);
1036
1037impl DebtCompletion {
1038 #[must_use]
1040 pub const fn clear() -> Self {
1041 Self(ClosureState::Clear)
1042 }
1043
1044 #[must_use]
1046 pub const fn observer_projection(debt: ClosureDebt, edge: ObserverProjection) -> Self {
1047 Self(ClosureState::Owed {
1048 debt,
1049 edge: StoredEdge::ObserverProjection(edge),
1050 })
1051 }
1052
1053 #[must_use]
1055 pub const fn physical_compaction(debt: ClosureDebt, edge: PhysicalCompaction) -> Self {
1056 Self(ClosureState::Owed {
1057 debt,
1058 edge: StoredEdge::PhysicalCompaction(edge),
1059 })
1060 }
1061
1062 #[must_use]
1064 pub const fn into_state(self) -> ClosureState {
1065 self.0
1066 }
1067}
1068
1069#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1112pub struct OrdinaryBindingAuthority {
1113 binding: ActiveBinding,
1114 through_seq: DeliverySeq,
1115}
1116
1117impl OrdinaryBindingAuthority {
1118 pub(crate) const fn new(binding: ActiveBinding, through_seq: DeliverySeq) -> Self {
1119 Self {
1120 binding,
1121 through_seq,
1122 }
1123 }
1124
1125 #[must_use]
1127 pub const fn binding(self) -> ActiveBinding {
1128 self.binding
1129 }
1130
1131 #[must_use]
1133 pub const fn through_seq(self) -> DeliverySeq {
1134 self.through_seq
1135 }
1136
1137 pub(crate) fn cursor_progressed(self, event: Event) -> Result<Self, Self> {
1147 let EventKind::CursorProgressed {
1148 participant_id,
1149 binding_epoch,
1150 progress:
1151 CursorProgressEvent::Normal {
1152 previous_cursor,
1153 through_seq,
1154 },
1155 ..
1156 } = event.0
1157 else {
1158 return Err(self);
1159 };
1160 self.participant_ack_progressed(
1161 self.binding.conversation_id,
1162 participant_id,
1163 binding_epoch,
1164 previous_cursor,
1165 through_seq,
1166 )
1167 }
1168
1169 pub(crate) fn participant_ack_progressed(
1171 self,
1172 conversation_id: ConversationId,
1173 participant_id: ParticipantId,
1174 binding_epoch: BindingEpoch,
1175 previous_cursor: DeliverySeq,
1176 through_seq: DeliverySeq,
1177 ) -> Result<Self, Self> {
1178 if conversation_id != self.binding.conversation_id
1179 || participant_id != self.binding.participant_id
1180 || binding_epoch != self.binding.binding_epoch
1181 || previous_cursor != self.through_seq
1182 || through_seq <= previous_cursor
1183 {
1184 return Err(self);
1185 }
1186 Ok(Self {
1187 through_seq,
1188 ..self
1189 })
1190 }
1191
1192 pub(crate) fn binding_fate(
1199 self,
1200 terminal: CommittedDiedTerminal,
1201 resulting_floor: DeliverySeq,
1202 ) -> Result<OrdinaryBindingFate, Self> {
1203 if terminal.participant_id() != self.binding.participant_id
1204 || terminal.conversation_id() != self.binding.conversation_id
1205 || terminal.binding_epoch() != self.binding.binding_epoch
1206 {
1207 return Err(self);
1208 }
1209 Ok(OrdinaryBindingFate {
1210 conversation_id: self.binding.conversation_id,
1211 through_seq: self.through_seq,
1212 resulting_floor,
1213 release: DetachedCursorRelease {
1214 participant_id: self.binding.participant_id,
1215 last_dead_binding_epoch: self.binding.binding_epoch,
1216 },
1217 })
1218 }
1219}
1220
1221#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1228pub struct OrdinaryBindingFate {
1229 conversation_id: ConversationId,
1230 through_seq: DeliverySeq,
1231 resulting_floor: DeliverySeq,
1232 release: DetachedCursorRelease,
1233}
1234
1235impl OrdinaryBindingFate {
1236 #[must_use]
1238 pub const fn conversation_id(self) -> ConversationId {
1239 self.conversation_id
1240 }
1241
1242 #[must_use]
1244 pub const fn through_seq(self) -> DeliverySeq {
1245 self.through_seq
1246 }
1247
1248 #[must_use]
1250 pub const fn participant_id(self) -> ParticipantId {
1251 self.release.participant_id
1252 }
1253
1254 #[must_use]
1256 pub const fn last_dead_binding_epoch(self) -> BindingEpoch {
1257 self.release.last_dead_binding_epoch
1258 }
1259
1260 #[must_use]
1262 pub const fn resulting_floor(self) -> DeliverySeq {
1263 self.resulting_floor
1264 }
1265 #[must_use]
1267 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
1268 ObserverProgressProjection::new(self.conversation_id, self.resulting_floor)
1269 }
1270
1271 #[must_use]
1273 pub const fn into_direct_state(self, debt: ClosureDebt) -> ClosureState {
1274 owed(debt, StoredEdge::DetachedCursorRelease(self.release))
1275 }
1276}
1277
1278#[derive(Debug, PartialEq, Eq)]
1284pub struct FencedAttachCommit {
1285 conversation_id: ConversationId,
1286 participant_id: ParticipantId,
1287 marker_delivery_seq: DeliverySeq,
1288 prior_binding_epoch: BindingEpoch,
1289 new_binding_epoch: BindingEpoch,
1290 next_state: ClosureState,
1291}
1292
1293impl FencedAttachCommit {
1294 #[must_use]
1296 pub const fn conversation_id(&self) -> ConversationId {
1297 self.conversation_id
1298 }
1299
1300 #[must_use]
1302 pub const fn participant_id(&self) -> ParticipantId {
1303 self.participant_id
1304 }
1305
1306 #[must_use]
1308 pub const fn marker_delivery_seq(&self) -> DeliverySeq {
1309 self.marker_delivery_seq
1310 }
1311
1312 #[must_use]
1314 pub const fn prior_binding_epoch(&self) -> BindingEpoch {
1315 self.prior_binding_epoch
1316 }
1317
1318 #[must_use]
1320 pub const fn new_binding_epoch(&self) -> BindingEpoch {
1321 self.new_binding_epoch
1322 }
1323
1324 #[must_use]
1326 pub const fn next_state(&self) -> ClosureState {
1327 self.next_state
1328 }
1329
1330 pub(super) fn restore_validated(
1335 recovery: DetachedCredentialRecovery,
1336 record_authority: &ValidatedMarkerRecord,
1337 debt: ClosureDebt,
1338 event: Event,
1339 successor: DebtCompletion,
1340 ) -> Option<Self> {
1341 if debt.value().is_zero()
1342 || record_authority.conversation_id() != recovery.conversation_id
1343 || record_authority.participant_id() != recovery.participant_id
1344 || record_authority.delivery_seq() != recovery.marker_delivery_seq
1345 || record_authority.target_binding()
1346 != super::FrontierBinding::Detached(recovery.prior_binding_epoch)
1347 || record_authority.occurrence()
1348 != super::claim_frontier::MarkerRecordOccurrence::Delivered
1349 {
1350 return None;
1351 }
1352 let EventKind::FencedRecoveryCommitted {
1353 participant_id,
1354 marker_delivery_seq,
1355 prior_binding_epoch,
1356 new_binding_epoch,
1357 ..
1358 } = event.0
1359 else {
1360 return None;
1361 };
1362 if participant_id != recovery.participant_id
1363 || marker_delivery_seq != recovery.marker_delivery_seq
1364 || prior_binding_epoch != recovery.prior_binding_epoch
1365 || !is_next_generation(prior_binding_epoch, new_binding_epoch)
1366 {
1367 return None;
1368 }
1369 Some(Self {
1370 conversation_id: recovery.conversation_id,
1371 participant_id,
1372 marker_delivery_seq,
1373 prior_binding_epoch,
1374 new_binding_epoch,
1375 next_state: successor.into_state(),
1376 })
1377 }
1378
1379 pub(super) fn recovered_binding_fate(
1392 self,
1393 event: Event,
1394 ) -> Result<RecoveredBindingFate, Box<Self>> {
1395 let EventKind::BindingFateObserved {
1396 participant_id,
1397 binding_epoch,
1398 resulting_floor,
1399 } = event.0
1400 else {
1401 return Err(Box::new(self));
1402 };
1403 if participant_id != self.participant_id || binding_epoch != self.new_binding_epoch {
1404 return Err(Box::new(self));
1405 }
1406 let ClosureState::Owed { debt, edge } = self.next_state else {
1407 return Err(Box::new(self));
1408 };
1409 let predecessor = match edge {
1410 StoredEdge::ObserverProjection(value) => {
1411 RecoveredStorageEdge::ObserverProjection(value)
1412 }
1413 StoredEdge::PhysicalCompaction(value) => {
1414 RecoveredStorageEdge::PhysicalCompaction(value)
1415 }
1416 _ => return Err(Box::new(self)),
1417 };
1418 Ok(RecoveredBindingFate {
1419 conversation_id: self.conversation_id,
1420 predecessor_debt: debt,
1421 predecessor,
1422 resulting_floor,
1423 release: DetachedCursorRelease {
1424 participant_id,
1425 last_dead_binding_epoch: binding_epoch,
1426 },
1427 })
1428 }
1429}
1430
1431#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1432enum RecoveredStorageEdge {
1433 ObserverProjection(ObserverProjection),
1434 PhysicalCompaction(PhysicalCompaction),
1435}
1436
1437impl RecoveredStorageEdge {
1438 const fn into_stored_edge(self) -> StoredEdge {
1439 match self {
1440 Self::ObserverProjection(value) => StoredEdge::ObserverProjection(value),
1441 Self::PhysicalCompaction(value) => StoredEdge::PhysicalCompaction(value),
1442 }
1443 }
1444}
1445
1446#[derive(Debug, PartialEq, Eq)]
1452pub struct RecoveredBindingFate {
1453 conversation_id: ConversationId,
1454 predecessor_debt: ClosureDebt,
1455 predecessor: RecoveredStorageEdge,
1456 resulting_floor: DeliverySeq,
1457 release: DetachedCursorRelease,
1458}
1459
1460impl RecoveredBindingFate {
1461 #[must_use]
1463 pub const fn conversation_id(&self) -> ConversationId {
1464 self.conversation_id
1465 }
1466
1467 #[must_use]
1469 pub const fn predecessor_state(&self) -> ClosureState {
1470 owed(self.predecessor_debt, self.predecessor.into_stored_edge())
1471 }
1472
1473 #[must_use]
1475 pub const fn participant_id(&self) -> ParticipantId {
1476 self.release.participant_id
1477 }
1478
1479 #[must_use]
1481 pub const fn last_dead_binding_epoch(&self) -> BindingEpoch {
1482 self.release.last_dead_binding_epoch
1483 }
1484
1485 #[must_use]
1487 pub const fn resulting_floor(&self) -> DeliverySeq {
1488 self.resulting_floor
1489 }
1490 #[must_use]
1492 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
1493 ObserverProgressProjection::new(self.conversation_id, self.resulting_floor)
1494 }
1495}
1496
1497#[derive(Debug, PartialEq, Eq)]
1504pub struct PendingRecoveredCursorRelease {
1505 debt: ClosureDebt,
1506 predecessor: RecoveredStorageEdge,
1507 release: DetachedCursorRelease,
1508}
1509
1510impl PendingRecoveredCursorRelease {
1511 #[must_use]
1513 pub const fn current_state(&self) -> ClosureState {
1514 owed(self.debt, self.predecessor.into_stored_edge())
1515 }
1516
1517 #[must_use]
1519 pub const fn participant_id(&self) -> ParticipantId {
1520 self.release.participant_id
1521 }
1522
1523 #[must_use]
1525 pub const fn last_dead_binding_epoch(&self) -> BindingEpoch {
1526 self.release.last_dead_binding_epoch
1527 }
1528}
1529
1530#[derive(Debug, PartialEq, Eq)]
1532pub struct RecoveredCursorRelease {
1533 debt: ClosureDebt,
1534 release: DetachedCursorRelease,
1535}
1536
1537impl RecoveredCursorRelease {
1538 #[must_use]
1540 pub const fn debt(&self) -> ClosureDebt {
1541 self.debt
1542 }
1543
1544 #[must_use]
1546 pub const fn edge(&self) -> DetachedCursorRelease {
1547 self.release
1548 }
1549
1550 #[must_use]
1552 pub const fn into_state(self) -> ClosureState {
1553 owed(self.debt, StoredEdge::DetachedCursorRelease(self.release))
1554 }
1555}
1556
1557#[derive(Debug, PartialEq, Eq)]
1559pub enum RecoveredBindingFateTransition {
1560 PendingStorage(PendingRecoveredCursorRelease),
1562 DetachedCursorRelease(RecoveredCursorRelease),
1564}
1565
1566#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1567enum SuccessorUse {
1568 ObserverCompletion,
1569 ObserverMarkerAppend,
1570 ObserverLeave,
1571 PhysicalCompletion,
1572 PhysicalCover,
1573 CursorGreaterAck,
1574}
1575
1576#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1582pub struct ProjectionCompactionSuccessor {
1583 predecessor: StoredEdge,
1584 event: Event,
1585 use_kind: SuccessorUse,
1586 state: ClosureState,
1587}
1588
1589#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1596pub enum CursorFateSuccessor {
1597 DetachedCredentialRecovery(DetachedCredentialRecovery),
1599 DetachedCursorRelease(DetachedCursorRelease),
1601}
1602
1603impl CursorFateSuccessor {
1604 #[must_use]
1606 pub const fn into_stored_edge(self) -> StoredEdge {
1607 match self {
1608 Self::DetachedCredentialRecovery(value) => {
1609 StoredEdge::DetachedCredentialRecovery(value)
1610 }
1611 Self::DetachedCursorRelease(value) => StoredEdge::DetachedCursorRelease(value),
1612 }
1613 }
1614}
1615
1616#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1618pub enum DetachedAttachRefusal {
1619 RecoveryFence,
1621 MarkerNotDelivered,
1623 MarkerMismatch,
1625 DeliveredMarkerAwaitingAck,
1627 EpisodeChurnLimit,
1629 StaleAuthority,
1631 NoBinding,
1633}
1634
1635#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1636enum DetachedClaimTarget {
1637 CredentialRecovery {
1638 marker_delivery_seq: DeliverySeq,
1639 binding_epoch: BindingEpoch,
1640 },
1641 MarkerRelease {
1642 marker_delivery_seq: DeliverySeq,
1643 binding_epoch: BindingEpoch,
1644 },
1645 CursorRelease {
1646 binding_epoch: BindingEpoch,
1647 },
1648}
1649
1650#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1656pub struct KClaimBackedDetachedLeave {
1657 participant_id: ParticipantId,
1658 target: DetachedClaimTarget,
1659 actual_charge: ResourceVector,
1660}
1661
1662impl KClaimBackedDetachedLeave {
1663 #[must_use]
1665 pub const fn participant_id(self) -> ParticipantId {
1666 self.participant_id
1667 }
1668
1669 #[must_use]
1671 pub const fn actual_charge(self) -> ResourceVector {
1672 self.actual_charge
1673 }
1674}
1675
1676#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1685pub struct Event(EventKind);
1686
1687#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1688enum EventKind {
1689 ProjectionCompleted {
1690 through_seq: DeliverySeq,
1691 },
1692 CompactionCompleted {
1693 from_floor: DeliverySeq,
1694 through_seq: DeliverySeq,
1695 resulting_floor: DeliverySeq,
1696 },
1697 MarkerAppended {
1698 marker_delivery_seq: DeliverySeq,
1699 resulting_projection_through: DeliverySeq,
1700 },
1701 MarkerDelivered {
1702 participant_id: ParticipantId,
1703 binding_epoch: BindingEpoch,
1704 marker_delivery_seq: DeliverySeq,
1705 },
1706 CursorProgressed {
1707 participant_index: ParticipantIndex,
1708 participant_id: ParticipantId,
1709 binding_epoch: BindingEpoch,
1710 progress: CursorProgressEvent,
1711 resulting_floor: DeliverySeq,
1712 },
1713 BindingFateObserved {
1714 participant_id: ParticipantId,
1715 binding_epoch: BindingEpoch,
1716 resulting_floor: DeliverySeq,
1717 },
1718 LeaveCommitted {
1719 participant_id: ParticipantId,
1720 authority: LeaveAuthority,
1721 resulting_floor: DeliverySeq,
1722 },
1723 FencedRecoveryCommitted {
1724 participant_id: ParticipantId,
1725 marker_delivery_seq: DeliverySeq,
1726 prior_binding_epoch: BindingEpoch,
1727 new_binding_epoch: BindingEpoch,
1728 resulting_floor: DeliverySeq,
1729 },
1730}
1731
1732#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1733enum CursorProgressEvent {
1734 Normal {
1735 previous_cursor: DeliverySeq,
1736 through_seq: DeliverySeq,
1737 },
1738 Marker {
1739 marker_delivery_seq: DeliverySeq,
1740 },
1741}
1742
1743#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1744enum LeaveAuthority {
1745 Live(BindingEpoch),
1746 Detached,
1747}
1748
1749impl Event {
1750 #[must_use]
1752 pub const fn projection_completed(through_seq: DeliverySeq) -> Self {
1753 Self(EventKind::ProjectionCompleted { through_seq })
1754 }
1755
1756 #[must_use]
1758 pub const fn compaction_completed(
1759 from_floor: DeliverySeq,
1760 through_seq: DeliverySeq,
1761 resulting_floor: DeliverySeq,
1762 ) -> Option<Self> {
1763 if from_floor <= through_seq && resulting_floor > through_seq {
1764 Some(Self(EventKind::CompactionCompleted {
1765 from_floor,
1766 through_seq,
1767 resulting_floor,
1768 }))
1769 } else {
1770 None
1771 }
1772 }
1773
1774 #[must_use]
1776 pub const fn marker_appended(
1777 marker_delivery_seq: DeliverySeq,
1778 resulting_projection_through: DeliverySeq,
1779 ) -> Self {
1780 Self(EventKind::MarkerAppended {
1781 marker_delivery_seq,
1782 resulting_projection_through,
1783 })
1784 }
1785
1786 #[must_use]
1788 pub const fn marker_delivered(
1789 participant_id: ParticipantId,
1790 binding_epoch: BindingEpoch,
1791 marker_delivery_seq: DeliverySeq,
1792 ) -> Self {
1793 Self(EventKind::MarkerDelivered {
1794 participant_id,
1795 binding_epoch,
1796 marker_delivery_seq,
1797 })
1798 }
1799
1800 #[must_use]
1806 pub const fn cursor_progressed(
1807 participant_id: ParticipantId,
1808 binding_epoch: BindingEpoch,
1809 previous_cursor: DeliverySeq,
1810 through_seq: DeliverySeq,
1811 resulting_floor: DeliverySeq,
1812 ) -> Option<Self> {
1813 if through_seq > previous_cursor {
1814 Some(Self(EventKind::CursorProgressed {
1815 participant_index: participant_id,
1816 participant_id,
1817 binding_epoch,
1818 progress: CursorProgressEvent::Normal {
1819 previous_cursor,
1820 through_seq,
1821 },
1822 resulting_floor,
1823 }))
1824 } else {
1825 None
1826 }
1827 }
1828
1829 #[must_use]
1832 pub const fn marker_acknowledged(
1833 participant_id: ParticipantId,
1834 binding_epoch: BindingEpoch,
1835 marker_delivery_seq: DeliverySeq,
1836 resulting_floor: DeliverySeq,
1837 ) -> Self {
1838 Self(EventKind::CursorProgressed {
1839 participant_index: participant_id,
1840 participant_id,
1841 binding_epoch,
1842 progress: CursorProgressEvent::Marker {
1843 marker_delivery_seq,
1844 },
1845 resulting_floor,
1846 })
1847 }
1848
1849 #[must_use]
1851 pub const fn binding_fate_observed(
1852 participant_id: ParticipantId,
1853 binding_epoch: BindingEpoch,
1854 resulting_floor: DeliverySeq,
1855 ) -> Self {
1856 Self(EventKind::BindingFateObserved {
1857 participant_id,
1858 binding_epoch,
1859 resulting_floor,
1860 })
1861 }
1862
1863 #[must_use]
1865 pub const fn live_leave_committed(
1866 participant_id: ParticipantId,
1867 binding_epoch: BindingEpoch,
1868 resulting_floor: DeliverySeq,
1869 ) -> Self {
1870 Self(EventKind::LeaveCommitted {
1871 participant_id,
1872 authority: LeaveAuthority::Live(binding_epoch),
1873 resulting_floor,
1874 })
1875 }
1876
1877 #[must_use]
1879 pub const fn detached_leave_committed(
1880 participant_id: ParticipantId,
1881 resulting_floor: DeliverySeq,
1882 ) -> Self {
1883 Self(EventKind::LeaveCommitted {
1884 participant_id,
1885 authority: LeaveAuthority::Detached,
1886 resulting_floor,
1887 })
1888 }
1889
1890 #[must_use]
1892 pub const fn fenced_recovery_committed(
1893 participant_id: ParticipantId,
1894 marker_delivery_seq: DeliverySeq,
1895 prior_binding_epoch: BindingEpoch,
1896 new_binding_epoch: BindingEpoch,
1897 resulting_floor: DeliverySeq,
1898 ) -> Self {
1899 Self(EventKind::FencedRecoveryCommitted {
1900 participant_id,
1901 marker_delivery_seq,
1902 prior_binding_epoch,
1903 new_binding_epoch,
1904 resulting_floor,
1905 })
1906 }
1907}
1908
1909impl ObserverProjection {
1910 #[must_use]
1916 #[allow(
1917 dead_code,
1918 reason = "the crate-owned binding-fate operation invokes this sealed OP transition"
1919 )]
1920 pub const fn apply_ordinary_binding_fate(
1921 self,
1922 resulting_debt: ClosureDebt,
1923 authority: OrdinaryBindingFate,
1924 ) -> PendingRecoveredCursorRelease {
1925 PendingRecoveredCursorRelease {
1926 debt: resulting_debt,
1927 predecessor: RecoveredStorageEdge::ObserverProjection(self),
1928 release: authority.release,
1929 }
1930 }
1931
1932 pub fn apply_recovered_binding_fate(
1943 self,
1944 debt: ClosureDebt,
1945 resulting_debt: ClosureDebt,
1946 authority: RecoveredBindingFate,
1947 ) -> Result<RecoveredBindingFateTransition, RecoveredBindingFate> {
1948 if authority.predecessor != RecoveredStorageEdge::ObserverProjection(self)
1949 || authority.predecessor_debt != debt
1950 {
1951 return Err(authority);
1952 }
1953 Ok(RecoveredBindingFateTransition::PendingStorage(
1954 PendingRecoveredCursorRelease {
1955 debt: resulting_debt,
1956 predecessor: RecoveredStorageEdge::ObserverProjection(self),
1957 release: authority.release,
1958 },
1959 ))
1960 }
1961
1962 pub fn complete_after_recovered_binding_fate(
1969 self,
1970 event: Event,
1971 resulting_debt: Option<ClosureDebt>,
1972 pending: PendingRecoveredCursorRelease,
1973 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
1974 self.complete_after_binding_fate(event, resulting_debt, pending)
1975 }
1976
1977 pub fn complete_after_ordinary_binding_fate(
1984 self,
1985 event: Event,
1986 resulting_debt: Option<ClosureDebt>,
1987 pending: PendingRecoveredCursorRelease,
1988 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
1989 self.complete_after_binding_fate(event, resulting_debt, pending)
1990 }
1991
1992 pub(crate) fn complete_after_binding_fate(
1999 self,
2000 event: Event,
2001 resulting_debt: Option<ClosureDebt>,
2002 pending: PendingRecoveredCursorRelease,
2003 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2004 if pending.predecessor != RecoveredStorageEdge::ObserverProjection(self)
2005 || projection_completion_boundary(self, event).is_none()
2006 {
2007 return Err(pending);
2008 }
2009 Ok(preserve_or_clear(
2010 resulting_debt,
2011 StoredEdge::DetachedCursorRelease(pending.release),
2012 ))
2013 }
2014
2015 #[must_use]
2017 pub const fn clear_after_completion(
2018 &self,
2019 event: &Event,
2020 ) -> Option<ProjectionCompactionSuccessor> {
2021 if projection_completion_boundary(*self, *event).is_some() {
2022 Some(ProjectionCompactionSuccessor {
2023 predecessor: StoredEdge::ObserverProjection(*self),
2024 event: *event,
2025 use_kind: SuccessorUse::ObserverCompletion,
2026 state: ClosureState::Clear,
2027 })
2028 } else {
2029 None
2030 }
2031 }
2032
2033 #[must_use]
2035 pub const fn strict_after_completion(
2036 &self,
2037 event: &Event,
2038 debt: ClosureDebt,
2039 edge: StoredEdge,
2040 successor_boundary: DeliverySeq,
2041 ) -> Option<ProjectionCompactionSuccessor> {
2042 let Some(completed_through) = projection_completion_boundary(*self, *event) else {
2043 return None;
2044 };
2045 if successor_boundary <= completed_through
2046 || !strict_edge_matches_boundary(edge, successor_boundary)
2047 {
2048 return None;
2049 }
2050 Some(ProjectionCompactionSuccessor {
2051 predecessor: StoredEdge::ObserverProjection(*self),
2052 event: *event,
2053 use_kind: SuccessorUse::ObserverCompletion,
2054 state: ClosureState::Owed { debt, edge },
2055 })
2056 }
2057
2058 pub fn complete(
2065 self,
2066 debt: ClosureDebt,
2067 event: Event,
2068 successor: ProjectionCompactionSuccessor,
2069 ) -> Result<ClosureState, ClosureState> {
2070 let original = owed(debt, StoredEdge::ObserverProjection(self));
2071 if successor.predecessor == StoredEdge::ObserverProjection(self)
2072 && successor.event == event
2073 && successor.use_kind == SuccessorUse::ObserverCompletion
2074 && projection_completion_boundary(self, event).is_some()
2075 {
2076 Ok(successor.state)
2077 } else {
2078 Err(original)
2079 }
2080 }
2081
2082 #[must_use]
2084 pub const fn later_projection_after_marker(
2085 &self,
2086 event: &Event,
2087 debt: ClosureDebt,
2088 successor: Self,
2089 ) -> Option<ProjectionCompactionSuccessor> {
2090 let EventKind::MarkerAppended {
2091 marker_delivery_seq,
2092 resulting_projection_through,
2093 } = event.0
2094 else {
2095 return None;
2096 };
2097 if marker_delivery_seq <= self.through_seq
2098 || resulting_projection_through < marker_delivery_seq
2099 || successor.through_seq != resulting_projection_through
2100 {
2101 return None;
2102 }
2103 Some(ProjectionCompactionSuccessor {
2104 predecessor: StoredEdge::ObserverProjection(*self),
2105 event: *event,
2106 use_kind: SuccessorUse::ObserverMarkerAppend,
2107 state: owed(debt, StoredEdge::ObserverProjection(successor)),
2108 })
2109 }
2110
2111 pub fn marker_appended(
2118 self,
2119 debt: ClosureDebt,
2120 event: Event,
2121 successor: ProjectionCompactionSuccessor,
2122 ) -> Result<ClosureState, ClosureState> {
2123 let original = owed(debt, StoredEdge::ObserverProjection(self));
2124 if successor.predecessor == StoredEdge::ObserverProjection(self)
2125 && successor.event == event
2126 && successor.use_kind == SuccessorUse::ObserverMarkerAppend
2127 {
2128 Ok(successor.state)
2129 } else {
2130 Err(original)
2131 }
2132 }
2133
2134 #[must_use]
2136 pub const fn later_projection_after_leave(
2137 &self,
2138 event: &Event,
2139 debt: ClosureDebt,
2140 successor: Self,
2141 ) -> Option<ProjectionCompactionSuccessor> {
2142 let EventKind::LeaveCommitted {
2143 resulting_floor, ..
2144 } = event.0
2145 else {
2146 return None;
2147 };
2148 if successor.through_seq <= self.through_seq || successor.through_seq < resulting_floor {
2149 return None;
2150 }
2151 Some(ProjectionCompactionSuccessor {
2152 predecessor: StoredEdge::ObserverProjection(*self),
2153 event: *event,
2154 use_kind: SuccessorUse::ObserverLeave,
2155 state: owed(debt, StoredEdge::ObserverProjection(successor)),
2156 })
2157 }
2158
2159 pub fn leave_with_later_projection(
2166 self,
2167 debt: ClosureDebt,
2168 event: Event,
2169 successor: ProjectionCompactionSuccessor,
2170 ) -> Result<ClosureState, ClosureState> {
2171 let original = owed(debt, StoredEdge::ObserverProjection(self));
2172 if successor.predecessor == StoredEdge::ObserverProjection(self)
2173 && successor.event == event
2174 && successor.use_kind == SuccessorUse::ObserverLeave
2175 {
2176 Ok(successor.state)
2177 } else {
2178 Err(original)
2179 }
2180 }
2181
2182 pub const fn independent_event(
2190 self,
2191 debt: ClosureDebt,
2192 event: Event,
2193 resulting_debt: Option<ClosureDebt>,
2194 ) -> Result<ClosureState, ClosureState> {
2195 let original = owed(debt, StoredEdge::ObserverProjection(self));
2196 if !matches!(
2197 event.0,
2198 EventKind::CursorProgressed { .. }
2199 | EventKind::BindingFateObserved { .. }
2200 | EventKind::LeaveCommitted { .. }
2201 ) {
2202 return Err(original);
2203 }
2204 Ok(preserve_or_clear(
2205 resulting_debt,
2206 StoredEdge::ObserverProjection(self),
2207 ))
2208 }
2209
2210 pub const fn charged_binding_change(
2217 self,
2218 debt: ClosureDebt,
2219 episode_churn_used: u64,
2220 delta_cycles: u64,
2221 episode_churn_limit: u64,
2222 resulting_debt: Option<ClosureDebt>,
2223 ) -> Result<ClosureState, (ClosureState, DetachedAttachRefusal)> {
2224 let original = owed(debt, StoredEdge::ObserverProjection(self));
2225 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2226 return Err((original, DetachedAttachRefusal::EpisodeChurnLimit));
2227 }
2228 Ok(preserve_or_clear(
2229 resulting_debt,
2230 StoredEdge::ObserverProjection(self),
2231 ))
2232 }
2233}
2234
2235impl PhysicalCompaction {
2236 #[must_use]
2238 #[allow(
2239 dead_code,
2240 reason = "the crate-owned binding-fate replay boundary invokes this sealed PC transition"
2241 )]
2242 pub(crate) const fn apply_ordinary_binding_fate(
2243 self,
2244 resulting_debt: ClosureDebt,
2245 authority: OrdinaryBindingFate,
2246 ) -> RecoveredBindingFateTransition {
2247 if authority.resulting_floor > self.through_seq {
2248 RecoveredBindingFateTransition::DetachedCursorRelease(RecoveredCursorRelease {
2249 debt: resulting_debt,
2250 release: authority.release,
2251 })
2252 } else {
2253 RecoveredBindingFateTransition::PendingStorage(PendingRecoveredCursorRelease {
2254 debt: resulting_debt,
2255 predecessor: RecoveredStorageEdge::PhysicalCompaction(self),
2256 release: authority.release,
2257 })
2258 }
2259 }
2260
2261 pub fn apply_recovered_binding_fate(
2272 self,
2273 debt: ClosureDebt,
2274 resulting_debt: ClosureDebt,
2275 authority: RecoveredBindingFate,
2276 ) -> Result<RecoveredBindingFateTransition, RecoveredBindingFate> {
2277 if authority.predecessor != RecoveredStorageEdge::PhysicalCompaction(self)
2278 || authority.predecessor_debt != debt
2279 {
2280 return Err(authority);
2281 }
2282 if authority.resulting_floor > self.through_seq {
2283 Ok(RecoveredBindingFateTransition::DetachedCursorRelease(
2284 RecoveredCursorRelease {
2285 debt: resulting_debt,
2286 release: authority.release,
2287 },
2288 ))
2289 } else {
2290 Ok(RecoveredBindingFateTransition::PendingStorage(
2291 PendingRecoveredCursorRelease {
2292 debt: resulting_debt,
2293 predecessor: RecoveredStorageEdge::PhysicalCompaction(self),
2294 release: authority.release,
2295 },
2296 ))
2297 }
2298 }
2299
2300 pub fn complete_after_recovered_binding_fate(
2307 self,
2308 event: Event,
2309 resulting_debt: Option<ClosureDebt>,
2310 pending: PendingRecoveredCursorRelease,
2311 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2312 self.complete_after_binding_fate(event, resulting_debt, pending)
2313 }
2314
2315 pub(crate) fn complete_after_binding_fate(
2322 self,
2323 event: Event,
2324 resulting_debt: Option<ClosureDebt>,
2325 pending: PendingRecoveredCursorRelease,
2326 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2327 if pending.predecessor != RecoveredStorageEdge::PhysicalCompaction(self)
2328 || physical_completion_floor(self, event).is_none()
2329 {
2330 return Err(pending);
2331 }
2332 Ok(preserve_or_clear(
2333 resulting_debt,
2334 StoredEdge::DetachedCursorRelease(pending.release),
2335 ))
2336 }
2337
2338 #[must_use]
2340 pub const fn clear_after_completion(
2341 &self,
2342 event: &Event,
2343 ) -> Option<ProjectionCompactionSuccessor> {
2344 if physical_completion_floor(*self, *event).is_some() {
2345 Some(ProjectionCompactionSuccessor {
2346 predecessor: StoredEdge::PhysicalCompaction(*self),
2347 event: *event,
2348 use_kind: SuccessorUse::PhysicalCompletion,
2349 state: ClosureState::Clear,
2350 })
2351 } else {
2352 None
2353 }
2354 }
2355
2356 #[must_use]
2358 pub const fn strict_after_completion(
2359 &self,
2360 event: &Event,
2361 debt: ClosureDebt,
2362 edge: StoredEdge,
2363 successor_boundary: DeliverySeq,
2364 ) -> Option<ProjectionCompactionSuccessor> {
2365 let Some(resulting_floor) = physical_completion_floor(*self, *event) else {
2366 return None;
2367 };
2368 if successor_boundary < resulting_floor
2369 || !strict_edge_matches_boundary(edge, successor_boundary)
2370 {
2371 return None;
2372 }
2373 Some(ProjectionCompactionSuccessor {
2374 predecessor: StoredEdge::PhysicalCompaction(*self),
2375 event: *event,
2376 use_kind: SuccessorUse::PhysicalCompletion,
2377 state: ClosureState::Owed { debt, edge },
2378 })
2379 }
2380
2381 pub fn complete(
2388 self,
2389 debt: ClosureDebt,
2390 event: Event,
2391 successor: ProjectionCompactionSuccessor,
2392 ) -> Result<ClosureState, ClosureState> {
2393 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2394 if successor.predecessor == StoredEdge::PhysicalCompaction(self)
2395 && successor.event == event
2396 && successor.use_kind == SuccessorUse::PhysicalCompletion
2397 && physical_completion_floor(self, event).is_some()
2398 {
2399 Ok(successor.state)
2400 } else {
2401 Err(original)
2402 }
2403 }
2404
2405 pub const fn marker_appended(
2413 self,
2414 debt: ClosureDebt,
2415 event: Event,
2416 ) -> Result<ClosureState, ClosureState> {
2417 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2418 let EventKind::MarkerAppended {
2419 marker_delivery_seq,
2420 resulting_projection_through,
2421 } = event.0
2422 else {
2423 return Err(original);
2424 };
2425 if marker_delivery_seq <= self.through_seq
2426 || resulting_projection_through < marker_delivery_seq
2427 {
2428 return Err(original);
2429 }
2430 Ok(original)
2431 }
2432
2433 pub const fn preserve_progress(
2441 self,
2442 debt: ClosureDebt,
2443 event: Event,
2444 resulting_debt: ClosureDebt,
2445 ) -> Result<ClosureState, ClosureState> {
2446 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2447 let Some(resulting_floor) = progress_event_floor(event) else {
2448 return Err(original);
2449 };
2450 if resulting_floor > self.through_seq {
2451 return Err(original);
2452 }
2453 Ok(owed(resulting_debt, StoredEdge::PhysicalCompaction(self)))
2454 }
2455
2456 #[must_use]
2458 pub const fn clear_after_progress(
2459 &self,
2460 event: &Event,
2461 ) -> Option<ProjectionCompactionSuccessor> {
2462 let Some(resulting_floor) = progress_event_floor(*event) else {
2463 return None;
2464 };
2465 if resulting_floor <= self.through_seq {
2466 return None;
2467 }
2468 Some(ProjectionCompactionSuccessor {
2469 predecessor: StoredEdge::PhysicalCompaction(*self),
2470 event: *event,
2471 use_kind: SuccessorUse::PhysicalCover,
2472 state: ClosureState::Clear,
2473 })
2474 }
2475
2476 #[must_use]
2478 pub const fn strict_after_progress(
2479 &self,
2480 event: &Event,
2481 debt: ClosureDebt,
2482 edge: StoredEdge,
2483 successor_boundary: DeliverySeq,
2484 ) -> Option<ProjectionCompactionSuccessor> {
2485 let Some(resulting_floor) = progress_event_floor(*event) else {
2486 return None;
2487 };
2488 if resulting_floor <= self.through_seq
2489 || successor_boundary < resulting_floor
2490 || !strict_edge_matches_boundary(edge, successor_boundary)
2491 {
2492 return None;
2493 }
2494 Some(ProjectionCompactionSuccessor {
2495 predecessor: StoredEdge::PhysicalCompaction(*self),
2496 event: *event,
2497 use_kind: SuccessorUse::PhysicalCover,
2498 state: ClosureState::Owed { debt, edge },
2499 })
2500 }
2501
2502 pub fn covered_by_progress(
2509 self,
2510 debt: ClosureDebt,
2511 event: Event,
2512 successor: ProjectionCompactionSuccessor,
2513 ) -> Result<ClosureState, ClosureState> {
2514 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2515 if successor.predecessor == StoredEdge::PhysicalCompaction(self)
2516 && successor.event == event
2517 && successor.use_kind == SuccessorUse::PhysicalCover
2518 {
2519 Ok(successor.state)
2520 } else {
2521 Err(original)
2522 }
2523 }
2524
2525 #[must_use]
2527 pub const fn unchanged(self, debt: ClosureDebt) -> ClosureState {
2528 owed(debt, StoredEdge::PhysicalCompaction(self))
2529 }
2530
2531 pub const fn charged_binding_change_preserving(
2538 self,
2539 debt: ClosureDebt,
2540 episode_churn_used: u64,
2541 delta_cycles: u64,
2542 episode_churn_limit: u64,
2543 resulting_floor: DeliverySeq,
2544 resulting_debt: ClosureDebt,
2545 ) -> Result<ClosureState, (ClosureState, DetachedAttachRefusal)> {
2546 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2547 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2548 return Err((original, DetachedAttachRefusal::EpisodeChurnLimit));
2549 }
2550 if resulting_floor > self.through_seq {
2551 return Err((original, DetachedAttachRefusal::StaleAuthority));
2552 }
2553 Ok(owed(resulting_debt, StoredEdge::PhysicalCompaction(self)))
2554 }
2555
2556 #[allow(clippy::too_many_arguments)]
2563 pub const fn charged_binding_change_covering(
2564 self,
2565 debt: ClosureDebt,
2566 episode_churn_used: u64,
2567 delta_cycles: u64,
2568 episode_churn_limit: u64,
2569 resulting_floor: DeliverySeq,
2570 resulting_debt: ClosureDebt,
2571 edge: StoredEdge,
2572 successor_boundary: DeliverySeq,
2573 ) -> Result<ClosureState, (ClosureState, DetachedAttachRefusal)> {
2574 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2575 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2576 return Err((original, DetachedAttachRefusal::EpisodeChurnLimit));
2577 }
2578 if resulting_floor <= self.through_seq
2579 || successor_boundary < resulting_floor
2580 || !strict_edge_matches_boundary(edge, successor_boundary)
2581 {
2582 return Err((original, DetachedAttachRefusal::StaleAuthority));
2583 }
2584 Ok(owed(resulting_debt, edge))
2585 }
2586}
2587
2588impl MarkerDelivery {
2589 pub fn delivered_progress(self, event: Event) -> Result<ParticipantCursorProgress, Self> {
2602 let EventKind::MarkerDelivered {
2603 participant_id,
2604 binding_epoch,
2605 marker_delivery_seq,
2606 } = event.0
2607 else {
2608 return Err(self);
2609 };
2610 if participant_id != self.participant_id
2611 || binding_epoch != self.binding_epoch
2612 || marker_delivery_seq != self.marker_delivery_seq
2613 {
2614 return Err(self);
2615 }
2616 Ok(ParticipantCursorProgress::Marker(CursorProgressMarker {
2617 conversation_id: self.conversation_id,
2618 participant_id,
2619 binding_epoch,
2620 through_seq: marker_delivery_seq,
2621 marker_delivery_seq,
2622 }))
2623 }
2624
2625 pub fn delivered(self, debt: ClosureDebt, event: Event) -> Result<ClosureState, ClosureState> {
2635 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2636 let Ok(progress) = self.delivered_progress(event) else {
2637 return Err(original);
2638 };
2639 Ok(owed(debt, StoredEdge::ParticipantCursorProgress(progress)))
2640 }
2641
2642 pub const fn lower_progress(
2650 self,
2651 debt: ClosureDebt,
2652 event: Event,
2653 resulting_debt: Option<ClosureDebt>,
2654 ) -> Result<ClosureState, ClosureState> {
2655 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2656 let is_lower = match event.0 {
2657 EventKind::CursorProgressed {
2658 progress: CursorProgressEvent::Normal { through_seq, .. },
2659 ..
2660 }
2661 | EventKind::ProjectionCompleted { through_seq } => {
2662 through_seq < self.marker_delivery_seq
2663 }
2664 EventKind::CompactionCompleted {
2665 through_seq,
2666 resulting_floor,
2667 ..
2668 } => {
2669 through_seq < self.marker_delivery_seq
2670 && resulting_floor <= self.marker_delivery_seq
2671 }
2672 _ => false,
2673 };
2674 if !is_lower {
2675 return Err(original);
2676 }
2677 Ok(preserve_or_clear(
2678 resulting_debt,
2679 StoredEdge::MarkerDelivery(self),
2680 ))
2681 }
2682
2683 pub fn binding_fate(
2697 self,
2698 debt: ClosureDebt,
2699 terminal: super::storage::RestoredBindingFateTerminal,
2700 event: Event,
2701 ) -> Result<ClosureState, ClosureState> {
2702 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2703 let EventKind::BindingFateObserved {
2704 participant_id,
2705 binding_epoch,
2706 ..
2707 } = event.0
2708 else {
2709 return Err(original);
2710 };
2711 if participant_id != self.participant_id || binding_epoch != self.binding_epoch {
2712 return Err(original);
2713 }
2714 if terminal.participant_id() != self.participant_id
2715 || terminal.binding_epoch() != self.binding_epoch
2716 || terminal.conversation_id() != self.conversation_id
2717 {
2718 return Err(original);
2719 }
2720 Ok(owed(
2721 debt,
2722 StoredEdge::DetachedMarkerRelease(DetachedMarkerRelease {
2723 conversation_id: self.conversation_id,
2724 participant_id,
2725 marker_delivery_seq: self.marker_delivery_seq,
2726 last_dead_binding_epoch: binding_epoch,
2727 provenance: DetachedMarkerReleaseProvenance::Fate,
2728 }),
2729 ))
2730 }
2731
2732 pub const fn retarget(
2739 self,
2740 new_binding_epoch: BindingEpoch,
2741 episode_churn_used: u64,
2742 delta_cycles: u64,
2743 episode_churn_limit: u64,
2744 ) -> Result<Self, (Self, DetachedAttachRefusal)> {
2745 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2746 return Err((self, DetachedAttachRefusal::EpisodeChurnLimit));
2747 }
2748 if !is_next_generation(self.binding_epoch, new_binding_epoch) {
2749 return Err((self, DetachedAttachRefusal::StaleAuthority));
2750 }
2751 Ok(Self {
2752 binding_epoch: new_binding_epoch,
2753 ..self
2754 })
2755 }
2756
2757 pub fn leave(
2764 self,
2765 debt: ClosureDebt,
2766 event: Event,
2767 successor: DebtCompletion,
2768 ) -> Result<ClosureState, ClosureState> {
2769 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2770 let EventKind::LeaveCommitted {
2771 participant_id,
2772 authority: LeaveAuthority::Live(binding_epoch),
2773 ..
2774 } = event.0
2775 else {
2776 return Err(original);
2777 };
2778 if participant_id != self.participant_id || binding_epoch != self.binding_epoch {
2779 return Err(original);
2780 }
2781 Ok(successor.into_state())
2782 }
2783}
2784
2785impl ParticipantCursorProgress {
2786 pub fn complete_ack(
2794 self,
2795 debt: ClosureDebt,
2796 event: Event,
2797 successor: DebtCompletion,
2798 ) -> Result<ClosureState, ClosureState> {
2799 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
2800 let exact = match (self, event.0) {
2801 (
2802 Self::Continuous(value),
2803 EventKind::CursorProgressed {
2804 participant_id,
2805 binding_epoch,
2806 progress: CursorProgressEvent::Normal { through_seq, .. },
2807 ..
2808 },
2809 ) => {
2810 participant_id == value.participant_id
2811 && binding_epoch == value.binding_epoch
2812 && through_seq == value.through_seq
2813 }
2814 (
2815 Self::Marker(value),
2816 EventKind::CursorProgressed {
2817 participant_id,
2818 binding_epoch,
2819 progress: CursorProgressEvent::Normal { through_seq, .. },
2820 ..
2821 },
2822 ) => {
2823 participant_id == value.participant_id
2824 && binding_epoch == value.binding_epoch
2825 && through_seq == value.through_seq
2826 }
2827 (
2828 Self::Marker(value),
2829 EventKind::CursorProgressed {
2830 participant_id,
2831 binding_epoch,
2832 progress:
2833 CursorProgressEvent::Marker {
2834 marker_delivery_seq,
2835 },
2836 ..
2837 },
2838 ) => {
2839 participant_id == value.participant_id
2840 && binding_epoch == value.binding_epoch
2841 && marker_delivery_seq == value.marker_delivery_seq
2842 }
2843 _ => false,
2844 };
2845 if exact {
2846 Ok(successor.into_state())
2847 } else {
2848 Err(original)
2849 }
2850 }
2851
2852 pub fn lesser_ack(
2859 self,
2860 debt: ClosureDebt,
2861 event: Event,
2862 resulting_debt: ClosureDebt,
2863 ) -> Result<ClosureState, ClosureState> {
2864 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
2865 let EventKind::CursorProgressed {
2866 participant_id,
2867 binding_epoch,
2868 progress: CursorProgressEvent::Normal { through_seq, .. },
2869 ..
2870 } = event.0
2871 else {
2872 return Err(original);
2873 };
2874 if participant_id != self.participant_id()
2875 || binding_epoch != self.binding_epoch()
2876 || through_seq >= self.through_seq()
2877 {
2878 return Err(original);
2879 }
2880 Ok(owed(
2881 resulting_debt,
2882 StoredEdge::ParticipantCursorProgress(self),
2883 ))
2884 }
2885
2886 #[must_use]
2888 pub fn clear_after_greater_ack(&self, event: &Event) -> Option<ProjectionCompactionSuccessor> {
2889 if !greater_ack_matches(*self, *event) {
2890 return None;
2891 }
2892 Some(ProjectionCompactionSuccessor {
2893 predecessor: StoredEdge::ParticipantCursorProgress(*self),
2894 event: *event,
2895 use_kind: SuccessorUse::CursorGreaterAck,
2896 state: ClosureState::Clear,
2897 })
2898 }
2899
2900 #[must_use]
2902 pub fn strict_after_greater_ack(
2903 &self,
2904 event: &Event,
2905 debt: ClosureDebt,
2906 edge: StoredEdge,
2907 successor_boundary: DeliverySeq,
2908 ) -> Option<ProjectionCompactionSuccessor> {
2909 if !greater_ack_matches(*self, *event)
2910 || successor_boundary <= cursor_event_boundary(*event)
2911 || !strict_edge_matches_boundary(edge, successor_boundary)
2912 {
2913 return None;
2914 }
2915 Some(ProjectionCompactionSuccessor {
2916 predecessor: StoredEdge::ParticipantCursorProgress(*self),
2917 event: *event,
2918 use_kind: SuccessorUse::CursorGreaterAck,
2919 state: ClosureState::Owed { debt, edge },
2920 })
2921 }
2922
2923 pub fn greater_ack(
2930 self,
2931 debt: ClosureDebt,
2932 event: Event,
2933 successor: ProjectionCompactionSuccessor,
2934 ) -> Result<ClosureState, ClosureState> {
2935 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
2936 if successor.predecessor == StoredEdge::ParticipantCursorProgress(self)
2937 && successor.event == event
2938 && successor.use_kind == SuccessorUse::CursorGreaterAck
2939 && greater_ack_matches(self, event)
2940 {
2941 Ok(successor.state)
2942 } else {
2943 Err(original)
2944 }
2945 }
2946
2947 #[must_use]
2949 pub const fn unchanged(self, debt: ClosureDebt) -> ClosureState {
2950 owed(debt, StoredEdge::ParticipantCursorProgress(self))
2951 }
2952
2953 pub const fn storage_progress(
2962 self,
2963 debt: ClosureDebt,
2964 event: Event,
2965 resulting_debt: Option<ClosureDebt>,
2966 ) -> Result<ClosureState, ClosureState> {
2967 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
2968 let valid = match event.0 {
2969 EventKind::ProjectionCompleted { .. } => true,
2970 EventKind::CompactionCompleted {
2971 through_seq,
2972 resulting_floor,
2973 ..
2974 } => match self.marker_delivery_seq() {
2975 None => true,
2976 Some(marker) => through_seq < marker && resulting_floor <= marker,
2977 },
2978 _ => false,
2979 };
2980 if !valid {
2981 return Err(original);
2982 }
2983 Ok(preserve_or_clear(
2984 resulting_debt,
2985 StoredEdge::ParticipantCursorProgress(self),
2986 ))
2987 }
2988
2989 pub fn binding_fate(
3001 self,
3002 debt: ClosureDebt,
3003 terminal: super::storage::RestoredBindingFateTerminal,
3004 event: Event,
3005 ) -> Result<CursorFateSuccessor, ClosureState> {
3006 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
3007 let Self::Marker(value) = self else {
3008 return Err(original);
3009 };
3010 let EventKind::BindingFateObserved {
3011 participant_id,
3012 binding_epoch,
3013 resulting_floor,
3014 ..
3015 } = event.0
3016 else {
3017 return Err(original);
3018 };
3019 if participant_id != value.participant_id || binding_epoch != value.binding_epoch {
3020 return Err(original);
3021 }
3022 if terminal.participant_id() != value.participant_id
3023 || terminal.binding_epoch() != value.binding_epoch
3024 || terminal.conversation_id() != value.conversation_id
3025 {
3026 return Err(original);
3027 }
3028 Ok(CursorFateSuccessor::DetachedCredentialRecovery(
3029 DetachedCredentialRecovery {
3030 conversation_id: value.conversation_id,
3031 participant_id: value.participant_id,
3032 marker_delivery_seq: value.marker_delivery_seq,
3033 prior_binding_epoch: value.binding_epoch,
3034 resulting_floor,
3035 },
3036 ))
3037 }
3038
3039 pub const fn retarget(
3046 self,
3047 new_binding_epoch: BindingEpoch,
3048 episode_churn_used: u64,
3049 delta_cycles: u64,
3050 episode_churn_limit: u64,
3051 ) -> Result<Self, (Self, DetachedAttachRefusal)> {
3052 let Self::Continuous(value) = self else {
3053 return Err((self, DetachedAttachRefusal::DeliveredMarkerAwaitingAck));
3054 };
3055 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
3056 return Err((self, DetachedAttachRefusal::EpisodeChurnLimit));
3057 }
3058 if !is_next_generation(value.binding_epoch, new_binding_epoch) {
3059 return Err((self, DetachedAttachRefusal::StaleAuthority));
3060 }
3061 Ok(Self::Continuous(CursorProgressContinuous {
3062 binding_epoch: new_binding_epoch,
3063 ..value
3064 }))
3065 }
3066
3067 pub fn leave(
3074 self,
3075 debt: ClosureDebt,
3076 event: Event,
3077 successor: DebtCompletion,
3078 ) -> Result<ClosureState, ClosureState> {
3079 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
3080 let EventKind::LeaveCommitted {
3081 participant_id,
3082 authority: LeaveAuthority::Live(binding_epoch),
3083 ..
3084 } = event.0
3085 else {
3086 return Err(original);
3087 };
3088 if participant_id != self.participant_id() || binding_epoch != self.binding_epoch() {
3089 return Err(original);
3090 }
3091 Ok(successor.into_state())
3092 }
3093}
3094
3095pub(super) struct FencedAttachRecordRefusal {
3098 record: ValidatedMarkerRecord,
3099}
3100
3101impl core::fmt::Debug for FencedAttachRecordRefusal {
3102 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3103 formatter.write_str("FencedAttachRecordRefusal { record: <linear> }")
3104 }
3105}
3106
3107impl FencedAttachRecordRefusal {
3108 pub(super) const fn into_record(self) -> ValidatedMarkerRecord {
3110 self.record
3111 }
3112}
3113
3114impl DetachedCredentialRecovery {
3115 #[must_use]
3117 pub const fn validate_leave_claim(
3118 &self,
3119 participant_id: ParticipantId,
3120 actual_charge: ResourceVector,
3121 remaining_k: ResourceVector,
3122 exit_claims: u64,
3123 ) -> Option<KClaimBackedDetachedLeave> {
3124 validate_detached_claim(
3125 self.participant_id,
3126 DetachedClaimTarget::CredentialRecovery {
3127 marker_delivery_seq: self.marker_delivery_seq,
3128 binding_epoch: self.prior_binding_epoch,
3129 },
3130 participant_id,
3131 actual_charge,
3132 remaining_k,
3133 exit_claims,
3134 )
3135 }
3136
3137 pub(super) fn fenced_attach(
3144 self,
3145 record_authority: ValidatedMarkerRecord,
3146 debt: ClosureDebt,
3147 event: Event,
3148 successor: DebtCompletion,
3149 ) -> Result<FencedAttachCommit, Box<FencedAttachRecordRefusal>> {
3150 let Some(proof) =
3151 FencedAttachCommit::restore_validated(self, &record_authority, debt, event, successor)
3152 else {
3153 return Err(Box::new(FencedAttachRecordRefusal {
3154 record: record_authority,
3155 }));
3156 };
3157 record_authority.consume();
3158 Ok(proof)
3159 }
3160
3161 pub fn detached_leave(
3168 self,
3169 debt: ClosureDebt,
3170 event: Event,
3171 evidence: KClaimBackedDetachedLeave,
3172 successor: DebtCompletion,
3173 ) -> Result<ClosureState, ClosureState> {
3174 let original = owed(debt, StoredEdge::DetachedCredentialRecovery(self));
3175 let EventKind::LeaveCommitted {
3176 participant_id,
3177 authority: LeaveAuthority::Detached,
3178 ..
3179 } = event.0
3180 else {
3181 return Err(original);
3182 };
3183 let target = DetachedClaimTarget::CredentialRecovery {
3184 marker_delivery_seq: self.marker_delivery_seq,
3185 binding_epoch: self.prior_binding_epoch,
3186 };
3187 if participant_id != self.participant_id
3188 || evidence.participant_id != self.participant_id
3189 || evidence.target != target
3190 {
3191 return Err(original);
3192 }
3193 Ok(successor.into_state())
3194 }
3195
3196 #[must_use]
3198 pub const fn ordinary_attach_refusal(self) -> DetachedAttachRefusal {
3199 let _ = self;
3200 DetachedAttachRefusal::RecoveryFence
3201 }
3202
3203 #[must_use]
3205 pub const fn marker_attach_refusal(
3206 self,
3207 presented_marker: DeliverySeq,
3208 ) -> Option<DetachedAttachRefusal> {
3209 if presented_marker == self.marker_delivery_seq {
3210 None
3211 } else {
3212 Some(DetachedAttachRefusal::MarkerMismatch)
3213 }
3214 }
3215
3216 #[must_use]
3218 pub const fn authority_superseded(self) -> (Self, DetachedAttachRefusal) {
3219 (self, DetachedAttachRefusal::StaleAuthority)
3220 }
3221
3222 pub const fn unrelated_event(
3230 self,
3231 debt: ClosureDebt,
3232 event: Event,
3233 resulting_debt: Option<ClosureDebt>,
3234 ) -> Result<ClosureState, ClosureState> {
3235 unrelated_detached_event(
3236 StoredEdge::DetachedCredentialRecovery(self),
3237 self.participant_id,
3238 debt,
3239 event,
3240 resulting_debt,
3241 )
3242 }
3243}
3244
3245mod sealed {
3246 pub trait Sealed {}
3247}
3248
3249pub trait LeaveOnlyEdge: sealed::Sealed + Sized + Copy {
3251 fn participant_id(self) -> ParticipantId;
3253
3254 fn validate_leave_claim(
3257 &self,
3258 participant_id: ParticipantId,
3259 actual_charge: ResourceVector,
3260 remaining_k: ResourceVector,
3261 exit_claims: u64,
3262 ) -> Option<KClaimBackedDetachedLeave>;
3263
3264 fn leave(
3271 self,
3272 debt: ClosureDebt,
3273 event: Event,
3274 evidence: KClaimBackedDetachedLeave,
3275 successor: DebtCompletion,
3276 ) -> Result<ClosureState, ClosureState>;
3277
3278 fn repeat_fate(self, event: Event) -> Result<Self, Self>;
3285
3286 fn authority_superseded(self) -> (Self, DetachedAttachRefusal) {
3288 (self, DetachedAttachRefusal::StaleAuthority)
3289 }
3290
3291 fn binding_required_refusal(self) -> DetachedAttachRefusal {
3293 let _ = self;
3294 DetachedAttachRefusal::NoBinding
3295 }
3296
3297 fn unrelated_event(
3305 self,
3306 debt: ClosureDebt,
3307 event: Event,
3308 resulting_debt: Option<ClosureDebt>,
3309 ) -> Result<ClosureState, ClosureState>;
3310}
3311
3312impl sealed::Sealed for DetachedMarkerRelease {}
3313impl sealed::Sealed for DetachedCursorRelease {}
3314
3315impl LeaveOnlyEdge for DetachedMarkerRelease {
3316 fn participant_id(self) -> ParticipantId {
3317 self.participant_id
3318 }
3319
3320 fn validate_leave_claim(
3321 &self,
3322 participant_id: ParticipantId,
3323 actual_charge: ResourceVector,
3324 remaining_k: ResourceVector,
3325 exit_claims: u64,
3326 ) -> Option<KClaimBackedDetachedLeave> {
3327 validate_detached_claim(
3328 self.participant_id,
3329 DetachedClaimTarget::MarkerRelease {
3330 marker_delivery_seq: self.marker_delivery_seq,
3331 binding_epoch: self.last_dead_binding_epoch,
3332 },
3333 participant_id,
3334 actual_charge,
3335 remaining_k,
3336 exit_claims,
3337 )
3338 }
3339
3340 fn leave(
3341 self,
3342 debt: ClosureDebt,
3343 event: Event,
3344 evidence: KClaimBackedDetachedLeave,
3345 successor: DebtCompletion,
3346 ) -> Result<ClosureState, ClosureState> {
3347 let original = owed(debt, StoredEdge::DetachedMarkerRelease(self));
3348 let EventKind::LeaveCommitted {
3349 participant_id,
3350 authority: LeaveAuthority::Detached,
3351 ..
3352 } = event.0
3353 else {
3354 return Err(original);
3355 };
3356 let target = DetachedClaimTarget::MarkerRelease {
3357 marker_delivery_seq: self.marker_delivery_seq,
3358 binding_epoch: self.last_dead_binding_epoch,
3359 };
3360 if participant_id != self.participant_id
3361 || evidence.participant_id != self.participant_id
3362 || evidence.target != target
3363 {
3364 return Err(original);
3365 }
3366 Ok(successor.into_state())
3367 }
3368
3369 fn repeat_fate(self, event: Event) -> Result<Self, Self> {
3370 let EventKind::BindingFateObserved {
3371 participant_id,
3372 binding_epoch,
3373 ..
3374 } = event.0
3375 else {
3376 return Err(self);
3377 };
3378 if participant_id == self.participant_id && binding_epoch == self.last_dead_binding_epoch {
3379 Ok(self)
3380 } else {
3381 Err(self)
3382 }
3383 }
3384
3385 fn unrelated_event(
3386 self,
3387 debt: ClosureDebt,
3388 event: Event,
3389 resulting_debt: Option<ClosureDebt>,
3390 ) -> Result<ClosureState, ClosureState> {
3391 unrelated_detached_event(
3392 StoredEdge::DetachedMarkerRelease(self),
3393 self.participant_id,
3394 debt,
3395 event,
3396 resulting_debt,
3397 )
3398 }
3399}
3400
3401impl DetachedMarkerRelease {
3402 #[must_use]
3404 pub const fn ordinary_attach_refusal(self) -> DetachedAttachRefusal {
3405 let _ = self;
3406 DetachedAttachRefusal::RecoveryFence
3407 }
3408
3409 #[must_use]
3412 pub const fn marker_attach_refusal(
3413 self,
3414 presented_marker: DeliverySeq,
3415 ) -> DetachedAttachRefusal {
3416 if presented_marker == self.marker_delivery_seq {
3417 DetachedAttachRefusal::MarkerNotDelivered
3418 } else {
3419 DetachedAttachRefusal::MarkerMismatch
3420 }
3421 }
3422}
3423
3424impl LeaveOnlyEdge for DetachedCursorRelease {
3425 fn participant_id(self) -> ParticipantId {
3426 self.participant_id
3427 }
3428
3429 fn validate_leave_claim(
3430 &self,
3431 participant_id: ParticipantId,
3432 actual_charge: ResourceVector,
3433 remaining_k: ResourceVector,
3434 exit_claims: u64,
3435 ) -> Option<KClaimBackedDetachedLeave> {
3436 validate_detached_claim(
3437 self.participant_id,
3438 DetachedClaimTarget::CursorRelease {
3439 binding_epoch: self.last_dead_binding_epoch,
3440 },
3441 participant_id,
3442 actual_charge,
3443 remaining_k,
3444 exit_claims,
3445 )
3446 }
3447
3448 fn leave(
3449 self,
3450 debt: ClosureDebt,
3451 event: Event,
3452 evidence: KClaimBackedDetachedLeave,
3453 successor: DebtCompletion,
3454 ) -> Result<ClosureState, ClosureState> {
3455 let original = owed(debt, StoredEdge::DetachedCursorRelease(self));
3456 let EventKind::LeaveCommitted {
3457 participant_id,
3458 authority: LeaveAuthority::Detached,
3459 ..
3460 } = event.0
3461 else {
3462 return Err(original);
3463 };
3464 let target = DetachedClaimTarget::CursorRelease {
3465 binding_epoch: self.last_dead_binding_epoch,
3466 };
3467 if participant_id != self.participant_id
3468 || evidence.participant_id != self.participant_id
3469 || evidence.target != target
3470 {
3471 return Err(original);
3472 }
3473 Ok(successor.into_state())
3474 }
3475
3476 fn repeat_fate(self, event: Event) -> Result<Self, Self> {
3477 let EventKind::BindingFateObserved {
3478 participant_id,
3479 binding_epoch,
3480 ..
3481 } = event.0
3482 else {
3483 return Err(self);
3484 };
3485 if participant_id == self.participant_id && binding_epoch == self.last_dead_binding_epoch {
3486 Ok(self)
3487 } else {
3488 Err(self)
3489 }
3490 }
3491
3492 fn unrelated_event(
3493 self,
3494 debt: ClosureDebt,
3495 event: Event,
3496 resulting_debt: Option<ClosureDebt>,
3497 ) -> Result<ClosureState, ClosureState> {
3498 unrelated_detached_event(
3499 StoredEdge::DetachedCursorRelease(self),
3500 self.participant_id,
3501 debt,
3502 event,
3503 resulting_debt,
3504 )
3505 }
3506}
3507
3508impl DetachedCursorRelease {
3509 #[must_use]
3511 pub const fn ordinary_attach_refusal(self) -> DetachedAttachRefusal {
3512 let _ = self;
3513 DetachedAttachRefusal::RecoveryFence
3514 }
3515
3516 #[must_use]
3518 pub const fn marker_attach_refusal(self) -> DetachedAttachRefusal {
3519 let _ = self;
3520 DetachedAttachRefusal::MarkerMismatch
3521 }
3522}
3523
3524const fn owed(debt: ClosureDebt, edge: StoredEdge) -> ClosureState {
3525 ClosureState::Owed { debt, edge }
3526}
3527
3528const fn preserve_or_clear(resulting_debt: Option<ClosureDebt>, edge: StoredEdge) -> ClosureState {
3529 match resulting_debt {
3530 Some(debt) => owed(debt, edge),
3531 None => ClosureState::Clear,
3532 }
3533}
3534
3535const fn projection_completion_boundary(
3536 edge: ObserverProjection,
3537 event: Event,
3538) -> Option<DeliverySeq> {
3539 let EventKind::ProjectionCompleted { through_seq } = event.0 else {
3540 return None;
3541 };
3542 if through_seq == edge.through_seq {
3543 Some(through_seq)
3544 } else {
3545 None
3546 }
3547}
3548
3549const fn physical_completion_floor(edge: PhysicalCompaction, event: Event) -> Option<DeliverySeq> {
3550 match event.0 {
3551 EventKind::CompactionCompleted {
3552 from_floor,
3553 through_seq,
3554 resulting_floor,
3555 } if from_floor == edge.from_floor
3556 && through_seq == edge.through_seq
3557 && resulting_floor > edge.through_seq =>
3558 {
3559 Some(resulting_floor)
3560 }
3561 _ => None,
3562 }
3563}
3564
3565const fn progress_event_floor(event: Event) -> Option<DeliverySeq> {
3566 match event.0 {
3567 EventKind::CursorProgressed {
3568 resulting_floor, ..
3569 }
3570 | EventKind::BindingFateObserved {
3571 resulting_floor, ..
3572 }
3573 | EventKind::LeaveCommitted {
3574 resulting_floor, ..
3575 } => Some(resulting_floor),
3576 _ => None,
3577 }
3578}
3579
3580const fn cursor_event_boundary(event: Event) -> DeliverySeq {
3581 match event.0 {
3582 EventKind::CursorProgressed {
3583 progress: CursorProgressEvent::Normal { through_seq, .. },
3584 ..
3585 } => through_seq,
3586 _ => 0,
3587 }
3588}
3589
3590fn greater_ack_matches(edge: ParticipantCursorProgress, event: Event) -> bool {
3591 let EventKind::CursorProgressed {
3592 participant_id,
3593 binding_epoch,
3594 progress:
3595 CursorProgressEvent::Normal {
3596 previous_cursor,
3597 through_seq,
3598 },
3599 ..
3600 } = event.0
3601 else {
3602 return false;
3603 };
3604 participant_id == edge.participant_id()
3605 && binding_epoch == edge.binding_epoch()
3606 && previous_cursor < edge.through_seq()
3607 && through_seq > edge.through_seq()
3608}
3609
3610const fn strict_edge_matches_boundary(edge: StoredEdge, boundary: DeliverySeq) -> bool {
3611 match edge {
3612 StoredEdge::ObserverProjection(value) => value.through_seq == boundary,
3613 StoredEdge::PhysicalCompaction(value) => value.through_seq == boundary,
3614 StoredEdge::MarkerDelivery(value) => value.marker_delivery_seq == boundary,
3615 StoredEdge::ParticipantCursorProgress(value) => value.through_seq() == boundary,
3616 StoredEdge::DetachedCredentialRecovery(_) => false,
3617 StoredEdge::DetachedMarkerRelease(value) => value.marker_delivery_seq == boundary,
3618 StoredEdge::DetachedCursorRelease(_) => true,
3622 }
3623}
3624
3625const fn charged_churn_fits(used: u64, delta: u64, limit: u64) -> bool {
3626 delta > 0 && widen_u64(used) + widen_u64(delta) <= widen_u64(limit)
3627}
3628
3629#[allow(clippy::cast_lossless)]
3630const fn widen_u64(value: u64) -> u128 {
3631 value as u128
3632}
3633
3634const fn is_next_generation(old: BindingEpoch, new: BindingEpoch) -> bool {
3635 match old.capability_generation.get().checked_add(1) {
3636 Some(expected) => new.capability_generation.get() == expected,
3637 None => false,
3638 }
3639}
3640
3641const fn validate_detached_claim(
3642 owner: ParticipantId,
3643 target: DetachedClaimTarget,
3644 participant_id: ParticipantId,
3645 actual_charge: ResourceVector,
3646 remaining_k: ResourceVector,
3647 exit_claims: u64,
3648) -> Option<KClaimBackedDetachedLeave> {
3649 if participant_id != owner
3650 || actual_charge.entries == 0
3651 || actual_charge.bytes == 0
3652 || actual_charge.entries > remaining_k.entries
3653 || actual_charge.bytes > remaining_k.bytes
3654 || exit_claims == 0
3655 {
3656 return None;
3657 }
3658 Some(KClaimBackedDetachedLeave {
3659 participant_id,
3660 target,
3661 actual_charge,
3662 })
3663}
3664
3665const fn event_participant(event: Event) -> Option<ParticipantId> {
3666 match event.0 {
3667 EventKind::MarkerDelivered { participant_id, .. }
3668 | EventKind::CursorProgressed { participant_id, .. }
3669 | EventKind::BindingFateObserved { participant_id, .. }
3670 | EventKind::LeaveCommitted { participant_id, .. }
3671 | EventKind::FencedRecoveryCommitted { participant_id, .. } => Some(participant_id),
3672 EventKind::ProjectionCompleted { .. }
3673 | EventKind::CompactionCompleted { .. }
3674 | EventKind::MarkerAppended { .. } => None,
3675 }
3676}
3677
3678const fn unrelated_detached_event(
3679 edge: StoredEdge,
3680 owner: ParticipantId,
3681 debt: ClosureDebt,
3682 event: Event,
3683 resulting_debt: Option<ClosureDebt>,
3684) -> Result<ClosureState, ClosureState> {
3685 let original = owed(debt, edge);
3686 let Some(participant_id) = event_participant(event) else {
3687 return Err(original);
3688 };
3689 if participant_id == owner {
3690 return Err(original);
3691 }
3692 Ok(preserve_or_clear(resulting_debt, edge))
3693}