Skip to main content

liminal_protocol/wire/
response.rs

1use alloc::{boxed::Box, string::String, vec::Vec};
2
3use crate::algebra::{ResourceDimension, ResourceVector};
4
5use super::{
6    AckGapReason, AckRegressionReason, AttachAttemptToken, AttachEnvelope, AttachSecret,
7    AttemptConflict, BindingEpoch, ClientDiscriminant, ClosureCheckedEnvelope, ConversationId,
8    Counter, DecodeClass, DeliverySeq, DetachAttemptToken, DetachEnvelope, EnrollmentEnvelope,
9    EnrollmentToken, Generation, IdentityCapacityScope, InvalidObserverEpochListReason,
10    InvalidObserverEpochReason, LeaveAttemptToken, LeaveEnvelope, MarkerAckEnvelope,
11    MarkerClosureCapacityExceeded, MarkerMismatchReason, MarkerNotDeliveredReason, ObserverEpoch,
12    ParticipantAckEnvelope, ParticipantId, ProtocolVersion, ReceiptCapacityScope,
13    ReceiptExpiryReason, RecordAdmissionAttemptToken, RecordAdmissionEnvelope, ResponseEnvelope,
14    SequenceBudget, ServerDiscriminant, SettlementEpoch,
15};
16
17pub use super::tags::{DetachAuthorityStateTag, LeaveAuthorityStateTag, ResourceDimensionTag};
18
19/// Exact pre-semantic participant transport rejection.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct ParticipantTransportRejected {
22    /// Selected exact reason body.
23    pub reason: TransportRejectionReason,
24}
25
26/// Closed transport-rejection reason union.
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub enum TransportRejectionReason {
29    /// Declared complete frame exceeds the current allocation bound.
30    FrameTooLarge {
31        /// Header plus declared payload bytes.
32        complete_frame_bytes: u64,
33        /// Active negotiated or pre-capability frame limit.
34        max_frame_bytes: u64,
35    },
36    /// Structural participant decoding failed.
37    DecodeFailed {
38        /// Exact structural failure class.
39        decode_class: DecodeClass,
40    },
41    /// Concrete participant version is unsupported.
42    UnsupportedVersion {
43        /// Version found in the inner prefix.
44        presented_version: ProtocolVersion,
45        /// Stored or server-supported expected version.
46        supported_version: ProtocolVersion,
47    },
48    /// Connection authentication failed.
49    AuthenticationFailed,
50    /// The connection did not negotiate participant capability.
51    ///
52    /// The serialized `required_capability` value is always exactly
53    /// `"participant-v1"`.
54    ParticipantCapabilityRequired,
55}
56
57/// An attempt token was reused with a different canonical body.
58///
59/// Admits credential attach, Leave, and — under §0.15 amendment A4 — ordinary
60/// record admission.
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum AttemptTokenBodyConflict {
63    /// Credential attach conflict; operation tag is fixed to attach.
64    CredentialAttach {
65        /// Presented attach token.
66        token: AttachAttemptToken,
67        /// Conversation from the conflicting request.
68        conversation_id: ConversationId,
69        /// Presented participant.
70        presented_participant_id: ParticipantId,
71        /// Presented generation.
72        presented_generation: Generation,
73        /// Presented marker option.
74        presented_marker_delivery_seq: Option<DeliverySeq>,
75        /// Generation or marker conflict, tested in that order.
76        conflict: AttemptConflict,
77    },
78    /// Leave conflict; only generation conflict is constructible.
79    Leave {
80        /// Presented Leave token.
81        token: LeaveAttemptToken,
82        /// Conversation from the conflicting request.
83        conversation_id: ConversationId,
84        /// Presented participant.
85        presented_participant_id: ParticipantId,
86        /// Presented generation.
87        presented_generation: Generation,
88    },
89    /// Ordinary record-admission conflict (§0.15 amendment A4).
90    ///
91    /// Carries no [`AttemptConflict`] selector, in the same way the Leave
92    /// variant carries no `presented_marker_delivery_seq`: the committed-identity
93    /// key is the (token, canonical-payload fingerprint, verified participant)
94    /// triple, so neither `Generation` nor `MarkerDeliverySequence` is
95    /// constructible on this arm. The one conflicting axis is the canonical body
96    /// this row is named for. Only the SAME-participant arm reaches the wire;
97    /// the cross-participant arm is silent forever (register law, A4).
98    RecordAdmission {
99        /// Presented record-admission token.
100        token: RecordAdmissionAttemptToken,
101        /// Conversation from the conflicting request.
102        conversation_id: ConversationId,
103        /// Presented participant, which is the committed identity's own.
104        presented_participant_id: ParticipantId,
105        /// Presented generation.
106        presented_generation: Generation,
107    },
108}
109
110/// Connection-conversation capacity refusal shared by two exact wire routes.
111///
112/// The semantic-request arm is carried by `0x0102`; the observer-recovery arm
113/// is carried by `0x0124`. Keeping both schemas under this one named outcome
114/// follows the frozen contract's R-D1 register while the variants prevent the
115/// two different bodies from being confused.
116#[derive(Clone, Debug, PartialEq, Eq)]
117pub enum ConnectionConversationCapacityExceeded {
118    /// Decoded semantic request with its exact common envelope (`0x0102`).
119    SemanticRequest {
120        /// Exact triggering request envelope.
121        request: ResponseEnvelope,
122        /// Negotiated connection-conversation limit.
123        limit: u64,
124    },
125    /// Observer-recovery request-index preflight refusal (`0x0124`).
126    ObserverRecovery {
127        /// First request-ordered conversation that would exceed the limit.
128        conversation_id: ConversationId,
129        /// Signed connection-conversation limit.
130        limit: u64,
131    },
132}
133
134/// Exact binding-slot occupancy response; occupying identity is never disclosed.
135#[derive(Clone, Debug, PartialEq, Eq)]
136pub enum ConnectionConversationBindingOccupied {
137    /// Enrollment; the encoded presented-participant option is exactly `None`.
138    Enrollment {
139        /// Conversation from the request.
140        conversation_id: ConversationId,
141        /// Enrollment token from the request.
142        enrollment_token: EnrollmentToken,
143    },
144    /// Credential attach; the encoded option is `Some(participant_id)`.
145    CredentialAttach {
146        /// Conversation from the request.
147        conversation_id: ConversationId,
148        /// Presented participant.
149        participant_id: ParticipantId,
150        /// Presented generation.
151        capability_generation: Generation,
152        /// Attach token from the request.
153        attach_attempt_token: AttachAttemptToken,
154        /// Presented marker option.
155        accept_marker_delivery_seq: Option<DeliverySeq>,
156    },
157}
158
159/// Request kinds that may require an unreserved transaction-order major.
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub enum OrderAllocatingEnvelope {
162    /// Enrollment.
163    Enrollment(EnrollmentEnvelope),
164    /// Credential attach.
165    CredentialAttach(AttachEnvelope),
166    /// Ordinary record admission.
167    RecordAdmission(RecordAdmissionEnvelope),
168}
169
170/// Exhausted conversation transaction order.
171#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct ConversationOrderExhausted {
173    /// Exact triggering request envelope.
174    request: OrderAllocatingEnvelope,
175    /// Current high allocated major.
176    high: u64,
177    /// Current unreserved majors remaining.
178    order_remaining: u128,
179    /// Current `A + X + RO + RA` claims.
180    reserved_claims: u128,
181    /// Simulated remaining majors.
182    resulting_order_remaining: u128,
183    /// Simulated four-term reserved claims.
184    resulting_reserved_claims: u128,
185}
186
187impl ConversationOrderExhausted {
188    /// Exact required-major count serialized by protocol v1.
189    pub const REQUIRED_MAJORS: u64 = 1;
190
191    /// Constructs the canonical order-exhaustion snapshot.
192    ///
193    /// The counter and checked next value are derived rather than accepted from
194    /// the caller, making `next_value = Some(high + 1)` (or `None` exactly at
195    /// `u64::MAX`) structural.
196    #[must_use]
197    pub const fn new(
198        request: OrderAllocatingEnvelope,
199        high: u64,
200        order_remaining: u128,
201        reserved_claims: u128,
202        resulting_order_remaining: u128,
203        resulting_reserved_claims: u128,
204    ) -> Self {
205        Self {
206            request,
207            high,
208            order_remaining,
209            reserved_claims,
210            resulting_order_remaining,
211            resulting_reserved_claims,
212        }
213    }
214
215    /// Exact triggering request envelope.
216    #[must_use]
217    pub const fn request(&self) -> &OrderAllocatingEnvelope {
218        &self.request
219    }
220
221    /// Fixed counter selector.
222    #[must_use]
223    pub const fn counter(&self) -> Counter {
224        let _ = self;
225        Counter::TransactionOrder
226    }
227
228    /// Current high allocated major.
229    #[must_use]
230    pub const fn high(&self) -> u64 {
231        self.high
232    }
233
234    /// Checked next major, absent exactly after allocation of `u64::MAX`.
235    #[must_use]
236    pub const fn next_value(&self) -> Option<u64> {
237        self.high.checked_add(1)
238    }
239
240    /// Current unreserved majors remaining.
241    #[must_use]
242    pub const fn order_remaining(&self) -> u128 {
243        self.order_remaining
244    }
245
246    /// Current `A + X + RO + RA` claims.
247    #[must_use]
248    pub const fn reserved_claims(&self) -> u128 {
249        self.reserved_claims
250    }
251
252    /// Simulated remaining majors.
253    #[must_use]
254    pub const fn resulting_order_remaining(&self) -> u128 {
255        self.resulting_order_remaining
256    }
257
258    /// Simulated four-term reserved claims.
259    #[must_use]
260    pub const fn resulting_reserved_claims(&self) -> u128 {
261        self.resulting_reserved_claims
262    }
263}
264
265/// Participant-naming envelopes eligible for unknown/retired classification.
266#[derive(Clone, Debug, PartialEq, Eq)]
267pub enum ParticipantReferenceEnvelope {
268    /// Credential attach.
269    CredentialAttach(AttachEnvelope),
270    /// Detach.
271    Detach(DetachEnvelope),
272    /// Continuous acknowledgement.
273    ParticipantAck(ParticipantAckEnvelope),
274    /// Leave.
275    Leave(LeaveEnvelope),
276    /// Marker acknowledgement.
277    MarkerAck(MarkerAckEnvelope),
278    /// Ordinary record admission.
279    RecordAdmission(RecordAdmissionEnvelope),
280}
281
282/// Binding-required request envelopes.
283#[derive(Clone, Debug, PartialEq, Eq)]
284pub enum BindingRequiredEnvelope {
285    /// Detach.
286    Detach(DetachEnvelope),
287    /// Continuous acknowledgement.
288    ParticipantAck(ParticipantAckEnvelope),
289    /// Bound Leave.
290    Leave(LeaveEnvelope),
291    /// Marker acknowledgement.
292    MarkerAck(MarkerAckEnvelope),
293    /// Ordinary record admission.
294    RecordAdmission(RecordAdmissionEnvelope),
295}
296
297/// Unknown participant outcome.
298#[derive(Clone, Debug, PartialEq, Eq)]
299pub struct ParticipantUnknown {
300    /// Exact triggering request envelope.
301    pub request: ParticipantReferenceEnvelope,
302}
303
304/// Missing required binding outcome.
305#[derive(Clone, Debug, PartialEq, Eq)]
306pub struct NoBinding {
307    /// Exact triggering request envelope.
308    pub request: BindingRequiredEnvelope,
309}
310
311/// Current binding view carried only by terminalized-detach authority.
312#[derive(Clone, Copy, Debug, PartialEq, Eq)]
313pub enum BindingStateView {
314    /// A current binding exists.
315    Bound {
316        /// Current binding epoch.
317        current_binding_epoch: BindingEpoch,
318    },
319    /// No current binding exists.
320    Detached,
321}
322
323impl BindingStateView {
324    /// Returns the exact nested binding-state tag.
325    #[must_use]
326    pub const fn tag(self) -> super::BindingStateTag {
327        match self {
328            Self::Bound { .. } => super::BindingStateTag::Bound,
329            Self::Detached => super::BindingStateTag::Detached,
330        }
331    }
332}
333
334/// Data retained by the mandated terminalized detach cell.
335///
336/// Fields are private. External callers can obtain this response only through
337/// the lifecycle module's verified terminalized-cell transition or wire decode.
338#[derive(Clone, Debug, PartialEq, Eq)]
339pub struct TerminalizedDetachCell {
340    conversation_id: ConversationId,
341    participant_id: ParticipantId,
342    capability_generation: Generation,
343    detach_attempt_token: DetachAttemptToken,
344    current_generation: Generation,
345    committed_binding_epoch: BindingEpoch,
346    binding_state: BindingStateView,
347}
348
349impl TerminalizedDetachCell {
350    /// Constructs the server-side semantic response from the mandated fourth
351    /// detach-cell variant.
352    ///
353    /// Taking [`crate::lifecycle::TerminalizedDetach`] here is intentional: the
354    /// three-variant detach model rejected by
355    /// `docs/design/LP-EXTRACTION-GOAL.md` cannot call this constructor because
356    /// it has no state carrying the old committed binding epoch.
357    pub(crate) const fn from_terminalized_state<V>(
358        state: &crate::lifecycle::TerminalizedDetach<V>,
359        conversation_id: ConversationId,
360        current_generation: Generation,
361        binding_state: BindingStateView,
362    ) -> Self {
363        Self {
364            conversation_id,
365            participant_id: state.participant_id(),
366            capability_generation: state.request_generation(),
367            detach_attempt_token: state.token(),
368            current_generation,
369            committed_binding_epoch: state.committed_binding_epoch(),
370            binding_state,
371        }
372    }
373
374    /// Reconstructs the same response from an already-selected wire union arm.
375    ///
376    /// The authority argument is constructible only inside the server-value
377    /// decoder. Ordinary semantic code must use [`Self::from_terminalized_state`],
378    /// preserving the compile-time guarantee mandated by
379    /// `docs/design/LP-EXTRACTION-GOAL.md`.
380    #[allow(clippy::too_many_arguments)]
381    pub(super) const fn from_wire_decode(
382        _authority: super::server_codec::TerminalizedWireDecodeAuthority,
383        conversation_id: ConversationId,
384        participant_id: ParticipantId,
385        capability_generation: Generation,
386        detach_attempt_token: DetachAttemptToken,
387        current_generation: Generation,
388        committed_binding_epoch: BindingEpoch,
389        binding_state: BindingStateView,
390    ) -> Self {
391        Self {
392            conversation_id,
393            participant_id,
394            capability_generation,
395            detach_attempt_token,
396            current_generation,
397            committed_binding_epoch,
398            binding_state,
399        }
400    }
401
402    #[cfg(test)]
403    pub(crate) const fn for_client_test(
404        conversation_id: ConversationId,
405        participant_id: ParticipantId,
406        capability_generation: Generation,
407        detach_attempt_token: DetachAttemptToken,
408        current_generation: Generation,
409        committed_binding_epoch: BindingEpoch,
410        binding_state: BindingStateView,
411    ) -> Self {
412        Self {
413            conversation_id,
414            participant_id,
415            capability_generation,
416            detach_attempt_token,
417            current_generation,
418            committed_binding_epoch,
419            binding_state,
420        }
421    }
422
423    /// Conversation from the old detach request.
424    #[must_use]
425    pub const fn conversation_id(&self) -> ConversationId {
426        self.conversation_id
427    }
428
429    /// Participant from the old detach request.
430    #[must_use]
431    pub const fn participant_id(&self) -> ParticipantId {
432        self.participant_id
433    }
434
435    /// Presented generation from the old detach request.
436    #[must_use]
437    pub const fn capability_generation(&self) -> Generation {
438        self.capability_generation
439    }
440
441    /// Old detach attempt token.
442    #[must_use]
443    pub const fn detach_attempt_token(&self) -> DetachAttemptToken {
444        self.detach_attempt_token
445    }
446
447    /// Current live generation.
448    #[must_use]
449    pub const fn current_generation(&self) -> Generation {
450        self.current_generation
451    }
452
453    /// Old committed binding epoch retained by terminalization.
454    #[must_use]
455    pub const fn committed_binding_epoch(&self) -> BindingEpoch {
456        self.committed_binding_epoch
457    }
458
459    /// Current bound/detached view.
460    #[must_use]
461    pub const fn binding_state(&self) -> BindingStateView {
462        self.binding_state
463    }
464}
465
466/// Detach-specific stale-authority tagged union.
467#[derive(Clone, Debug, PartialEq, Eq)]
468pub enum DetachStaleAuthority {
469    /// Ordinary live generation mismatch.
470    Live {
471        /// Conversation from the request.
472        conversation_id: ConversationId,
473        /// Participant from the request.
474        participant_id: ParticipantId,
475        /// Presented generation.
476        capability_generation: Generation,
477        /// Presented detach token.
478        detach_attempt_token: DetachAttemptToken,
479        /// Current generation.
480        current_generation: Generation,
481    },
482    /// Verified exact old token resolved to a terminalized detach cell.
483    TerminalizedDetachCell(TerminalizedDetachCell),
484}
485
486impl DetachStaleAuthority {
487    /// Returns the detach-specific outer authority-state tag.
488    #[must_use]
489    pub const fn authority_state_tag(&self) -> DetachAuthorityStateTag {
490        match self {
491            Self::Live { .. } => DetachAuthorityStateTag::Live,
492            Self::TerminalizedDetachCell(_) => DetachAuthorityStateTag::TerminalizedDetachCell,
493        }
494    }
495}
496
497/// Leave-specific stale-authority tagged union.
498#[derive(Clone, Debug, PartialEq, Eq)]
499pub enum LeaveStaleAuthority {
500    /// Live generation or secret mismatch.
501    Live {
502        /// Conversation from the request.
503        conversation_id: ConversationId,
504        /// Participant from the request.
505        participant_id: ParticipantId,
506        /// Presented generation.
507        presented_generation: Generation,
508        /// Presented Leave token.
509        leave_attempt_token: LeaveAttemptToken,
510        /// Current generation.
511        current_generation: Generation,
512    },
513    /// Exact committed Leave token with a mismatching secret.
514    CommittedLeaveTombstone {
515        /// Conversation from the request.
516        conversation_id: ConversationId,
517        /// Participant from the request.
518        participant_id: ParticipantId,
519        /// Presented generation.
520        presented_generation: Generation,
521        /// Presented Leave token.
522        leave_attempt_token: LeaveAttemptToken,
523        /// Permanent retired generation.
524        retired_generation: Generation,
525    },
526}
527
528impl LeaveStaleAuthority {
529    /// Returns the Leave-specific outer authority-state tag.
530    #[must_use]
531    pub const fn authority_state_tag(&self) -> LeaveAuthorityStateTag {
532        match self {
533            Self::Live { .. } => LeaveAuthorityStateTag::Live,
534            Self::CommittedLeaveTombstone { .. } => LeaveAuthorityStateTag::CommittedLeaveTombstone,
535        }
536    }
537}
538
539/// Common-envelope live stale-authority alternatives.
540#[derive(Clone, Debug, PartialEq, Eq)]
541pub enum CommonStaleAuthorityEnvelope {
542    /// Credential attach.
543    CredentialAttach(AttachEnvelope),
544    /// Continuous acknowledgement.
545    ParticipantAck(ParticipantAckEnvelope),
546    /// Marker acknowledgement.
547    MarkerAck(MarkerAckEnvelope),
548    /// Ordinary record admission.
549    RecordAdmission(RecordAdmissionEnvelope),
550}
551
552/// Complete stale-authority outcome payload.
553#[derive(Clone, Debug, PartialEq, Eq)]
554pub enum StaleAuthority {
555    /// Generic live authority mismatch.
556    Live {
557        /// Exact triggering request envelope.
558        request: CommonStaleAuthorityEnvelope,
559        /// Current live generation.
560        current_generation: Generation,
561    },
562    /// Detach-specific complete replacement schema.
563    Detach(DetachStaleAuthority),
564    /// Leave-specific complete replacement schema.
565    Leave(LeaveStaleAuthority),
566}
567
568/// Tombstone classification, including enrollment's additional participant id.
569#[derive(Clone, Debug, PartialEq, Eq)]
570pub enum Retired {
571    /// Enrollment mapping resolved to a tombstone.
572    Enrollment {
573        /// Original request envelope.
574        request: EnrollmentEnvelope,
575        /// Mapped permanent participant.
576        participant_id: ParticipantId,
577        /// Permanent retired generation.
578        retired_generation: Generation,
579    },
580    /// Participant-naming request resolved to its tombstone.
581    Participant {
582        /// Exact triggering request envelope.
583        request: ParticipantReferenceEnvelope,
584        /// Permanent retired generation.
585        retired_generation: Generation,
586    },
587}
588
589/// Canonical successful enrollment receipt.
590#[derive(Clone, Debug, PartialEq, Eq)]
591pub struct EnrollBound {
592    conversation_id: ConversationId,
593    token: EnrollmentToken,
594    participant_id: ParticipantId,
595    attach_secret: AttachSecret,
596    origin_binding_epoch: BindingEpoch,
597    receipt_expires_at: u128,
598    provenance_expires_at: u128,
599}
600
601impl EnrollBound {
602    /// Creates an enrollment result only for the protocol's fixed generation 1.
603    ///
604    /// Returns `None` when the origin binding epoch does not carry generation 1.
605    /// The required wire fields `request_generation`, `persisted_cursor`, and
606    /// `accepted_marker_delivery_seq` are synthesized as `None`, zero, and
607    /// `None`, respectively.
608    #[must_use]
609    pub const fn new(
610        conversation_id: ConversationId,
611        token: EnrollmentToken,
612        participant_id: ParticipantId,
613        attach_secret: AttachSecret,
614        origin_binding_epoch: BindingEpoch,
615        receipt_expires_at: u128,
616        provenance_expires_at: u128,
617    ) -> Option<Self> {
618        if origin_binding_epoch.capability_generation.get() == 1 {
619            Some(Self {
620                conversation_id,
621                token,
622                participant_id,
623                attach_secret,
624                origin_binding_epoch,
625                receipt_expires_at,
626                provenance_expires_at,
627            })
628        } else {
629            None
630        }
631    }
632
633    /// Conversation from the request.
634    #[must_use]
635    pub const fn conversation_id(&self) -> ConversationId {
636        self.conversation_id
637    }
638
639    /// Enrollment token echoed as the result token.
640    #[must_use]
641    pub const fn token(&self) -> EnrollmentToken {
642        self.token
643    }
644
645    /// Minted participant.
646    #[must_use]
647    pub const fn participant_id(&self) -> ParticipantId {
648        self.participant_id
649    }
650
651    /// Required absent request generation.
652    #[must_use]
653    pub const fn request_generation(&self) -> Option<Generation> {
654        None
655    }
656
657    /// Fixed enrollment result generation 1.
658    #[must_use]
659    pub const fn capability_generation(&self) -> Generation {
660        self.origin_binding_epoch.capability_generation
661    }
662
663    /// Newly minted attach secret.
664    #[must_use]
665    pub const fn attach_secret(&self) -> AttachSecret {
666        self.attach_secret
667    }
668
669    /// Origin binding epoch.
670    #[must_use]
671    pub const fn origin_binding_epoch(&self) -> BindingEpoch {
672        self.origin_binding_epoch
673    }
674
675    /// Fixed persisted cursor zero.
676    #[must_use]
677    pub const fn persisted_cursor(&self) -> DeliverySeq {
678        0
679    }
680
681    /// Required absent accepted-marker field.
682    #[must_use]
683    pub const fn accepted_marker_delivery_seq(&self) -> Option<DeliverySeq> {
684        None
685    }
686
687    /// Receipt deadline.
688    #[must_use]
689    pub const fn receipt_expires_at(&self) -> u128 {
690        self.receipt_expires_at
691    }
692
693    /// Provenance deadline.
694    #[must_use]
695    pub const fn provenance_expires_at(&self) -> u128 {
696        self.provenance_expires_at
697    }
698}
699
700/// Enrollment token maps to a known live participant.
701#[derive(Clone, Debug, PartialEq, Eq)]
702pub struct EnrollmentKnown {
703    /// Conversation from the request.
704    pub conversation_id: ConversationId,
705    /// Enrollment token.
706    pub token: EnrollmentToken,
707    /// Mapped participant.
708    pub participant_id: ParticipantId,
709    /// Current live generation.
710    pub current_generation: Generation,
711}
712
713/// Exact expired/superseded receipt response.
714#[derive(Clone, Debug, PartialEq, Eq)]
715pub enum ReceiptExpired {
716    /// Enrollment provenance; marker field is structurally absent.
717    Enrollment {
718        /// Conversation from the request.
719        conversation_id: ConversationId,
720        /// Enrollment token.
721        token: EnrollmentToken,
722        /// Mapped participant.
723        participant_id: ParticipantId,
724        /// Result generation retained by provenance.
725        result_generation: Generation,
726        /// Current live generation.
727        current_generation: Generation,
728        /// Deadline or supersession.
729        reason: ReceiptExpiryReason,
730    },
731    /// Credential-attach provenance.
732    CredentialAttach {
733        /// Conversation from the request.
734        conversation_id: ConversationId,
735        /// Attach token.
736        token: AttachAttemptToken,
737        /// Participant from the request.
738        participant_id: ParticipantId,
739        /// Originally presented generation.
740        presented_generation: Generation,
741        /// Originally presented marker option.
742        presented_marker_delivery_seq: Option<DeliverySeq>,
743        /// Result generation retained by provenance.
744        result_generation: Generation,
745        /// Current live generation.
746        current_generation: Generation,
747        /// Deadline or supersession.
748        reason: ReceiptExpiryReason,
749    },
750}
751
752/// Receipt/provenance scopes reachable from enrollment.
753///
754/// Per-participant occupancy is zero before a new identity exists, so the two
755/// per-participant refusal arms are deliberately unconstructible here.
756#[derive(Clone, Copy, Debug, PartialEq, Eq)]
757pub enum EnrollmentReceiptCapacityScope {
758    /// Server-wide live receipt rows.
759    LiveReceiptServer,
760    /// Server-wide provenance rows.
761    ProvenanceServer,
762    /// Per-conversation provenance rows.
763    ProvenanceConversation,
764}
765
766impl EnrollmentReceiptCapacityScope {
767    /// Returns the shared five-value wire registry entry.
768    #[must_use]
769    pub const fn wire_scope(self) -> ReceiptCapacityScope {
770        match self {
771            Self::LiveReceiptServer => ReceiptCapacityScope::LiveReceiptServer,
772            Self::ProvenanceServer => ReceiptCapacityScope::ProvenanceServer,
773            Self::ProvenanceConversation => ReceiptCapacityScope::ProvenanceConversation,
774        }
775    }
776}
777
778/// Receipt/provenance capacity refusal with origin-specific valid scopes.
779#[derive(Clone, Debug, PartialEq, Eq)]
780pub enum ReceiptCapacityExceeded {
781    /// Enrollment capacity refusal.
782    Enrollment {
783        /// Enrollment request envelope.
784        request: EnrollmentEnvelope,
785        /// One of the three scopes reachable before identity mint.
786        scope: EnrollmentReceiptCapacityScope,
787        /// Signed scope limit.
788        limit: u64,
789        /// Current occupancy.
790        occupied: u64,
791    },
792    /// Credential-attach capacity refusal.
793    CredentialAttach {
794        /// Credential-attach request envelope.
795        request: AttachEnvelope,
796        /// First full scope in the exact five-scope order.
797        scope: ReceiptCapacityScope,
798        /// Signed scope limit.
799        limit: u64,
800        /// Current occupancy.
801        occupied: u64,
802    },
803}
804
805impl ReceiptCapacityExceeded {
806    /// Exact requested-row count serialized by protocol v1.
807    pub const REQUESTED: u64 = 1;
808}
809
810/// Enrollment identity-capacity refusal.
811#[derive(Clone, Debug, PartialEq, Eq)]
812pub struct IdentityCapacityExceeded {
813    /// Enrollment request envelope.
814    pub request: EnrollmentEnvelope,
815    /// Server or conversation scope.
816    pub scope: IdentityCapacityScope,
817    /// Signed scope limit.
818    pub limit: u64,
819    /// Current occupancy.
820    pub occupied: u64,
821}
822
823impl IdentityCapacityExceeded {
824    /// Exact requested-identity count serialized by protocol v1.
825    pub const REQUESTED: u64 = 1;
826}
827
828/// Common observer-backpressure suffix.
829#[derive(Clone, Copy, Debug, PartialEq, Eq)]
830pub struct ObserverBackpressureState {
831    /// Refusal epoch.
832    backpressure_epoch: ObserverEpoch,
833    /// Observer progress captured by the refusal.
834    observer_progress: DeliverySeq,
835}
836
837impl ObserverBackpressureState {
838    /// Constructs an initial refusal at the current observer-progress baseline.
839    ///
840    /// An initial refusal epoch is exactly the progress value observed by the
841    /// serialized operation.
842    #[must_use]
843    pub const fn initial(observer_progress: DeliverySeq) -> Self {
844        Self {
845            backpressure_epoch: observer_progress,
846            observer_progress,
847        }
848    }
849
850    /// Reconstructs an exact-token replay refusal at its current baseline.
851    ///
852    /// Pending replay at greater progress must drain or atomically rewrite the
853    /// cell epoch to that progress before responding. It therefore returns
854    /// `None` for any inequality.
855    #[must_use]
856    pub const fn replay(
857        backpressure_epoch: ObserverEpoch,
858        observer_progress: DeliverySeq,
859    ) -> Option<Self> {
860        if backpressure_epoch == observer_progress {
861            Some(Self {
862                backpressure_epoch,
863                observer_progress,
864            })
865        } else {
866            None
867        }
868    }
869
870    /// Refusal epoch serialized in the response.
871    #[must_use]
872    pub const fn backpressure_epoch(self) -> ObserverEpoch {
873        self.backpressure_epoch
874    }
875
876    /// Observer progress captured by the refusal.
877    #[must_use]
878    pub const fn observer_progress(self) -> DeliverySeq {
879        self.observer_progress
880    }
881}
882
883/// Exact operation-specific observer-backpressure payload.
884#[derive(Clone, Debug, PartialEq, Eq)]
885pub enum ObserverBackpressure {
886    /// Enrollment.
887    Enrollment {
888        /// Request envelope.
889        request: EnrollmentEnvelope,
890        /// Refusal state.
891        state: ObserverBackpressureState,
892    },
893    /// Credential attach.
894    CredentialAttach {
895        /// Request envelope.
896        request: AttachEnvelope,
897        /// Refusal state.
898        state: ObserverBackpressureState,
899    },
900    /// Detach, which additionally exposes the committed old binding epoch.
901    Detach {
902        /// Request envelope.
903        request: DetachEnvelope,
904        /// Binding epoch the detach is terminalizing.
905        committed_binding_epoch: BindingEpoch,
906        /// Refusal state.
907        state: ObserverBackpressureState,
908    },
909    /// Leave, which additionally reports whether an older terminal cell exists.
910    Leave {
911        /// Request envelope.
912        request: LeaveEnvelope,
913        /// Refusal state.
914        state: ObserverBackpressureState,
915        /// Whether an earlier terminal cell exists.
916        prior_terminal_cell_exists: bool,
917    },
918    /// Ordinary admission.
919    RecordAdmission {
920        /// Request envelope.
921        request: RecordAdmissionEnvelope,
922        /// Refusal state.
923        state: ObserverBackpressureState,
924    },
925}
926
927/// Marker candidate awaiting its drain, refusing a membership-validated request.
928///
929/// The attach and detach wrappers of `apply_live_transition` validate
930/// conversation membership before the seam, so this row may carry the settlement
931/// epoch and is paired with the `0x0202 MarkerSettled` wake. Answering this
932/// condition with [`ObserverBackpressure`] is OUTLAWED — it would promise an
933/// `ObserverProgressed` that nothing sends (participant contract §0.16
934/// condition 2, attach and detach wrappers).
935///
936/// Retry discipline mirrors the stage-11 row: persist the waiting state, retry
937/// once after a matching `MarkerSettled` or reconnect status. `refused_epoch` is
938/// load-bearing rather than decorative — the retry matches it against the wake's
939/// own epoch.
940#[derive(Clone, Copy, Debug, PartialEq, Eq)]
941pub enum MarkerSettlementBackpressure {
942    /// Credential attach.
943    CredentialAttach {
944        /// Conversation from the refused request.
945        conversation_id: ConversationId,
946        /// Settlement epoch this refusal waits on.
947        refused_epoch: SettlementEpoch,
948    },
949    /// Explicit detach.
950    Detach {
951        /// Conversation from the refused request.
952        conversation_id: ConversationId,
953        /// Settlement epoch this refusal waits on.
954        refused_epoch: SettlementEpoch,
955    },
956}
957
958/// Marker candidate awaiting its drain, refusing a subsequent enrollment.
959///
960/// NO epoch label and NO pushed event of any kind, by ratified law rather than
961/// by omission: the enrollment wrapper has no membership predicate — an
962/// `EnrollmentRequest` carries only `{ conversation_id, enrollment_token }` and
963/// that token is a replay-dedup key, not a capability — so the wrapper cannot
964/// distinguish an invited enrollee from a stranger, and a wake or an epoch label
965/// on this arm would be granted to both by construction. The enroller retries at
966/// its own cadence (participant contract §0.16 condition 2, enrollment wrapper;
967/// the no-polling law is answered there, not evaded here).
968#[derive(Clone, Copy, Debug, PartialEq, Eq)]
969pub struct EnrollmentSettlementBackpressure {
970    /// Conversation from the refused request.
971    pub conversation_id: ConversationId,
972}
973
974/// Request alternatives that can exhaust optional sequence admission.
975#[derive(Clone, Debug, PartialEq, Eq)]
976pub enum SequenceAllocatingEnvelope {
977    /// Enrollment.
978    Enrollment(EnrollmentEnvelope),
979    /// Credential attach.
980    CredentialAttach(AttachEnvelope),
981    /// Ordinary record admission.
982    RecordAdmission(RecordAdmissionEnvelope),
983}
984
985/// Canonical sequence-exhaustion response.
986#[derive(Clone, Debug, PartialEq, Eq)]
987pub struct ConversationSequenceExhausted {
988    /// Exact triggering request envelope.
989    pub request: SequenceAllocatingEnvelope,
990    /// Exactly one canonical ten-field budget.
991    pub sequence_budget: SequenceBudget,
992}
993
994/// Canonical successful credential-attach receipt.
995#[derive(Clone, Debug, PartialEq, Eq)]
996pub struct AttachBound {
997    /// Conversation from the request.
998    conversation_id: ConversationId,
999    /// Attach token echoed as the result token.
1000    token: AttachAttemptToken,
1001    /// Participant from the request.
1002    participant_id: ParticipantId,
1003    /// Originally presented generation.
1004    request_generation: Generation,
1005    /// Newly minted attach secret.
1006    attach_secret: AttachSecret,
1007    /// Origin binding epoch.
1008    origin_binding_epoch: BindingEpoch,
1009    /// Persisted participant cursor.
1010    persisted_cursor: DeliverySeq,
1011    /// Marker accepted atomically by recovery.
1012    accepted_marker_delivery_seq: Option<DeliverySeq>,
1013    /// Receipt deadline.
1014    receipt_expires_at: u128,
1015    /// Provenance deadline.
1016    provenance_expires_at: u128,
1017}
1018
1019impl AttachBound {
1020    /// Constructs an ordinary attach receipt.
1021    ///
1022    /// Returns `None` unless the origin epoch carries the exact checked
1023    /// successor of `request_generation`. Ordinary attach structurally records
1024    /// no accepted marker and preserves the supplied cursor.
1025    #[must_use]
1026    #[allow(clippy::too_many_arguments)]
1027    pub const fn ordinary(
1028        conversation_id: ConversationId,
1029        token: AttachAttemptToken,
1030        participant_id: ParticipantId,
1031        request_generation: Generation,
1032        attach_secret: AttachSecret,
1033        origin_binding_epoch: BindingEpoch,
1034        persisted_cursor: DeliverySeq,
1035        receipt_expires_at: u128,
1036        provenance_expires_at: u128,
1037    ) -> Option<Self> {
1038        if !is_successor_generation(
1039            request_generation,
1040            origin_binding_epoch.capability_generation,
1041        ) {
1042            return None;
1043        }
1044        Some(Self {
1045            conversation_id,
1046            token,
1047            participant_id,
1048            request_generation,
1049            attach_secret,
1050            origin_binding_epoch,
1051            persisted_cursor,
1052            accepted_marker_delivery_seq: None,
1053            receipt_expires_at,
1054            provenance_expires_at,
1055        })
1056    }
1057
1058    /// Constructs a fenced-recovery attach receipt.
1059    ///
1060    /// Returns `None` unless the origin epoch carries the exact checked
1061    /// successor of `request_generation`. The accepted marker is also the
1062    /// resulting persisted cursor by construction.
1063    #[must_use]
1064    #[allow(clippy::too_many_arguments)]
1065    pub const fn fenced(
1066        conversation_id: ConversationId,
1067        token: AttachAttemptToken,
1068        participant_id: ParticipantId,
1069        request_generation: Generation,
1070        attach_secret: AttachSecret,
1071        origin_binding_epoch: BindingEpoch,
1072        accepted_marker_delivery_seq: DeliverySeq,
1073        receipt_expires_at: u128,
1074        provenance_expires_at: u128,
1075    ) -> Option<Self> {
1076        if !is_successor_generation(
1077            request_generation,
1078            origin_binding_epoch.capability_generation,
1079        ) {
1080            return None;
1081        }
1082        Some(Self {
1083            conversation_id,
1084            token,
1085            participant_id,
1086            request_generation,
1087            attach_secret,
1088            origin_binding_epoch,
1089            persisted_cursor: accepted_marker_delivery_seq,
1090            accepted_marker_delivery_seq: Some(accepted_marker_delivery_seq),
1091            receipt_expires_at,
1092            provenance_expires_at,
1093        })
1094    }
1095
1096    /// Conversation from the request.
1097    #[must_use]
1098    pub const fn conversation_id(&self) -> ConversationId {
1099        self.conversation_id
1100    }
1101
1102    /// Attach token echoed as the result token.
1103    #[must_use]
1104    pub const fn token(&self) -> AttachAttemptToken {
1105        self.token
1106    }
1107
1108    /// Participant from the request.
1109    #[must_use]
1110    pub const fn participant_id(&self) -> ParticipantId {
1111        self.participant_id
1112    }
1113
1114    /// Originally presented generation.
1115    #[must_use]
1116    pub const fn request_generation(&self) -> Generation {
1117        self.request_generation
1118    }
1119
1120    /// Exact successor capability generation.
1121    #[must_use]
1122    pub const fn capability_generation(&self) -> Generation {
1123        self.origin_binding_epoch.capability_generation
1124    }
1125
1126    /// Newly minted attach secret.
1127    #[must_use]
1128    pub const fn attach_secret(&self) -> AttachSecret {
1129        self.attach_secret
1130    }
1131
1132    /// Origin binding epoch carrying the result generation.
1133    #[must_use]
1134    pub const fn origin_binding_epoch(&self) -> BindingEpoch {
1135        self.origin_binding_epoch
1136    }
1137
1138    /// Persisted participant cursor.
1139    #[must_use]
1140    pub const fn persisted_cursor(&self) -> DeliverySeq {
1141        self.persisted_cursor
1142    }
1143
1144    /// Marker accepted atomically by recovery, if this was the fenced path.
1145    #[must_use]
1146    pub const fn accepted_marker_delivery_seq(&self) -> Option<DeliverySeq> {
1147        self.accepted_marker_delivery_seq
1148    }
1149
1150    /// Receipt deadline.
1151    #[must_use]
1152    pub const fn receipt_expires_at(&self) -> u128 {
1153        self.receipt_expires_at
1154    }
1155
1156    /// Provenance deadline.
1157    #[must_use]
1158    pub const fn provenance_expires_at(&self) -> u128 {
1159        self.provenance_expires_at
1160    }
1161}
1162
1163const fn is_successor_generation(previous: Generation, successor: Generation) -> bool {
1164    match previous.get().checked_add(1) {
1165        Some(expected) => successor.get() == expected,
1166        None => false,
1167    }
1168}
1169
1170/// Attach receipt is no longer known after provenance expiry.
1171#[derive(Clone, Debug, PartialEq, Eq)]
1172pub struct StaleOrUnknownReceipt {
1173    /// Conversation from the request.
1174    pub conversation_id: ConversationId,
1175    /// Attach token.
1176    pub token: AttachAttemptToken,
1177    /// Participant from the request.
1178    pub participant_id: ParticipantId,
1179    /// Originally presented generation.
1180    pub presented_generation: Generation,
1181    /// Originally presented marker option.
1182    pub presented_marker_delivery_seq: Option<DeliverySeq>,
1183    /// Current live generation.
1184    pub current_generation: Generation,
1185}
1186
1187/// Attach marker-proof request fields; attach token is part of the replacement schema.
1188#[derive(Clone, Debug, PartialEq, Eq)]
1189pub struct AttachMarkerProof {
1190    /// Conversation from the request.
1191    pub conversation_id: ConversationId,
1192    /// Attach token from the request.
1193    pub token: AttachAttemptToken,
1194    /// Participant from the request.
1195    pub participant_id: ParticipantId,
1196    /// Presented generation.
1197    pub capability_generation: Generation,
1198    /// Explicit requested marker.
1199    pub requested_marker_delivery_seq: DeliverySeq,
1200}
1201
1202/// Marker-ack proof request fields.
1203#[derive(Clone, Debug, PartialEq, Eq)]
1204pub struct MarkerAckProof {
1205    /// Conversation from the request.
1206    pub conversation_id: ConversationId,
1207    /// Participant from the request.
1208    pub participant_id: ParticipantId,
1209    /// Presented generation.
1210    pub capability_generation: Generation,
1211    /// Explicit requested marker.
1212    pub requested_marker_delivery_seq: DeliverySeq,
1213}
1214
1215/// Marker-proof request alternatives.
1216#[derive(Clone, Debug, PartialEq, Eq)]
1217pub enum MarkerProofRequest {
1218    /// Credential attach proof.
1219    CredentialAttach(AttachMarkerProof),
1220    /// Marker acknowledgement proof.
1221    MarkerAck(MarkerAckProof),
1222}
1223
1224/// Requested marker was not delivered to the proof epoch.
1225#[derive(Clone, Debug, PartialEq, Eq)]
1226pub struct MarkerNotDelivered {
1227    /// Complete flattened request fields.
1228    pub request: MarkerProofRequest,
1229    /// Singleton reason tag.
1230    pub reason: MarkerNotDeliveredReason,
1231    /// Marker actually expected by current state.
1232    pub expected_marker_delivery_seq: DeliverySeq,
1233}
1234
1235/// Exact marker-mismatch reason body; no optional field bag exists.
1236#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1237pub enum MarkerMismatchBody {
1238    /// Requested marker is below current cursor.
1239    BelowCursor {
1240        /// Current participant cursor.
1241        current_cursor: DeliverySeq,
1242    },
1243    /// No marker is expected by current state.
1244    NoMarkerExpected,
1245    /// A different marker is expected.
1246    ExpectedDifferentMarker {
1247        /// Expected marker sequence.
1248        expected_marker_delivery_seq: DeliverySeq,
1249    },
1250}
1251
1252impl MarkerMismatchBody {
1253    /// Returns the stable reason selector.
1254    #[must_use]
1255    pub const fn reason(self) -> MarkerMismatchReason {
1256        match self {
1257            Self::BelowCursor { .. } => MarkerMismatchReason::BelowCursor,
1258            Self::NoMarkerExpected => MarkerMismatchReason::NoMarkerExpected,
1259            Self::ExpectedDifferentMarker { .. } => MarkerMismatchReason::ExpectedDifferentMarker,
1260        }
1261    }
1262}
1263
1264/// Presented marker does not match current marker state.
1265#[derive(Clone, Debug, PartialEq, Eq)]
1266pub struct MarkerMismatch {
1267    /// Complete flattened request fields.
1268    pub request: MarkerProofRequest,
1269    /// Selected exact reason body.
1270    pub mismatch: MarkerMismatchBody,
1271}
1272
1273/// Complete canonical receipt replay payload used by Bound/UnboundReceipt.
1274#[derive(Clone, Debug, PartialEq, Eq)]
1275pub enum ReceiptReplay {
1276    /// Enrollment canonical receipt; generation/cursor/marker constants are
1277    /// enforced by [`EnrollBound`].
1278    Enrollment(EnrollBound),
1279    /// Credential-attach canonical receipt; successor generation and
1280    /// cursor/marker relations are enforced by [`AttachBound`].
1281    CredentialAttach(AttachBound),
1282}
1283
1284/// Stable committed detach response.
1285#[derive(Clone, Debug, PartialEq, Eq)]
1286pub struct DetachCommitted {
1287    /// Conversation from the request.
1288    conversation_id: ConversationId,
1289    /// Participant from the request.
1290    participant_id: ParticipantId,
1291    /// Committed detach token.
1292    detach_attempt_token: DetachAttemptToken,
1293    /// Binding epoch ended by detach.
1294    committed_binding_epoch: BindingEpoch,
1295    /// Assigned Detached delivery sequence.
1296    detached_delivery_seq: DeliverySeq,
1297}
1298
1299impl DetachCommitted {
1300    /// Constructs a detach result and derives its presented generation from
1301    /// the binding epoch it ended.
1302    #[must_use]
1303    pub const fn new(
1304        conversation_id: ConversationId,
1305        participant_id: ParticipantId,
1306        detach_attempt_token: DetachAttemptToken,
1307        committed_binding_epoch: BindingEpoch,
1308        detached_delivery_seq: DeliverySeq,
1309    ) -> Self {
1310        Self {
1311            conversation_id,
1312            participant_id,
1313            detach_attempt_token,
1314            committed_binding_epoch,
1315            detached_delivery_seq,
1316        }
1317    }
1318
1319    /// Conversation from the request.
1320    #[must_use]
1321    pub const fn conversation_id(&self) -> ConversationId {
1322        self.conversation_id
1323    }
1324
1325    /// Participant from the request.
1326    #[must_use]
1327    pub const fn participant_id(&self) -> ParticipantId {
1328        self.participant_id
1329    }
1330
1331    /// Presented generation, equal to the committed binding epoch generation.
1332    #[must_use]
1333    pub const fn capability_generation(&self) -> Generation {
1334        self.committed_binding_epoch.capability_generation
1335    }
1336
1337    /// Committed detach token.
1338    #[must_use]
1339    pub const fn detach_attempt_token(&self) -> DetachAttemptToken {
1340        self.detach_attempt_token
1341    }
1342
1343    /// Binding epoch ended by detach.
1344    #[must_use]
1345    pub const fn committed_binding_epoch(&self) -> BindingEpoch {
1346        self.committed_binding_epoch
1347    }
1348
1349    /// Assigned Detached delivery sequence.
1350    #[must_use]
1351    pub const fn detached_delivery_seq(&self) -> DeliverySeq {
1352        self.detached_delivery_seq
1353    }
1354}
1355
1356/// Different detach token encountered an existing pending cell.
1357#[derive(Clone, Debug, PartialEq, Eq)]
1358pub struct DetachInProgress {
1359    /// Conversation from the competing request.
1360    pub conversation_id: ConversationId,
1361    /// Participant from the competing request.
1362    pub participant_id: ParticipantId,
1363    /// Competing presented token; stored token is never disclosed.
1364    pub presented_token: DetachAttemptToken,
1365    /// Competing presented generation.
1366    pub presented_generation: Generation,
1367    /// Binding epoch being terminalized by the pending cell.
1368    pub committed_binding_epoch: BindingEpoch,
1369}
1370
1371/// Continuous acknowledgement advanced the cursor.
1372#[derive(Clone, Debug, PartialEq, Eq)]
1373pub struct AckCommitted {
1374    /// Request envelope.
1375    request: ParticipantAckEnvelope,
1376}
1377
1378impl AckCommitted {
1379    /// Constructs a committed acknowledgement whose cursor is the requested
1380    /// cumulative boundary.
1381    #[must_use]
1382    pub const fn new(request: ParticipantAckEnvelope) -> Self {
1383        Self { request }
1384    }
1385
1386    /// Request envelope.
1387    #[must_use]
1388    pub const fn request(&self) -> &ParticipantAckEnvelope {
1389        &self.request
1390    }
1391
1392    /// Resulting committed cursor, equal to `request.through_seq`.
1393    #[must_use]
1394    pub const fn current_cursor(&self) -> DeliverySeq {
1395        self.request.through_seq
1396    }
1397}
1398
1399/// Idempotent normal or marker acknowledgement.
1400#[derive(Clone, Debug, PartialEq, Eq)]
1401pub enum AckNoOp {
1402    /// Continuous acknowledgement.
1403    ParticipantAck(ParticipantAckEnvelope),
1404    /// Marker acknowledgement.
1405    MarkerAck(MarkerAckEnvelope),
1406}
1407
1408impl AckNoOp {
1409    /// Constructs an idempotent continuous acknowledgement at its requested
1410    /// cursor.
1411    #[must_use]
1412    pub const fn participant_ack(request: ParticipantAckEnvelope) -> Self {
1413        Self::ParticipantAck(request)
1414    }
1415
1416    /// Constructs an idempotent marker acknowledgement at its requested
1417    /// marker cursor.
1418    #[must_use]
1419    pub const fn marker_ack(request: MarkerAckEnvelope) -> Self {
1420        Self::MarkerAck(request)
1421    }
1422
1423    /// Unchanged cursor, derived from the selected request envelope.
1424    #[must_use]
1425    pub const fn current_cursor(&self) -> DeliverySeq {
1426        match self {
1427            Self::ParticipantAck(request) => request.through_seq,
1428            Self::MarkerAck(request) => request.marker_delivery_seq,
1429        }
1430    }
1431}
1432
1433/// Continuous acknowledgement crossed a gap.
1434#[derive(Clone, Debug, PartialEq, Eq)]
1435pub struct AckGap {
1436    /// Request envelope.
1437    request: ParticipantAckEnvelope,
1438    /// Unchanged cursor.
1439    current_cursor: DeliverySeq,
1440}
1441
1442impl AckGap {
1443    /// Constructs a gap refusal for a requested boundary above the unchanged
1444    /// cursor.
1445    #[must_use]
1446    pub const fn new(request: ParticipantAckEnvelope, current_cursor: DeliverySeq) -> Option<Self> {
1447        if request.through_seq > current_cursor {
1448            Some(Self {
1449                request,
1450                current_cursor,
1451            })
1452        } else {
1453            None
1454        }
1455    }
1456
1457    /// Request envelope.
1458    #[must_use]
1459    pub const fn request(&self) -> &ParticipantAckEnvelope {
1460        &self.request
1461    }
1462
1463    /// Unchanged cursor.
1464    #[must_use]
1465    pub const fn current_cursor(&self) -> DeliverySeq {
1466        self.current_cursor
1467    }
1468
1469    /// Fixed gap reason.
1470    #[must_use]
1471    pub const fn reason(&self) -> AckGapReason {
1472        let _ = self;
1473        AckGapReason::NotContiguouslyAvailable
1474    }
1475}
1476
1477/// Continuous acknowledgement regressed below the cursor.
1478#[derive(Clone, Debug, PartialEq, Eq)]
1479pub struct AckRegression {
1480    /// Request envelope.
1481    request: ParticipantAckEnvelope,
1482    /// Unchanged cursor.
1483    current_cursor: DeliverySeq,
1484}
1485
1486impl AckRegression {
1487    /// Constructs a regression refusal for a requested boundary below the
1488    /// unchanged cursor.
1489    #[must_use]
1490    pub const fn new(request: ParticipantAckEnvelope, current_cursor: DeliverySeq) -> Option<Self> {
1491        if request.through_seq < current_cursor {
1492            Some(Self {
1493                request,
1494                current_cursor,
1495            })
1496        } else {
1497            None
1498        }
1499    }
1500
1501    /// Request envelope.
1502    #[must_use]
1503    pub const fn request(&self) -> &ParticipantAckEnvelope {
1504        &self.request
1505    }
1506
1507    /// Unchanged cursor.
1508    #[must_use]
1509    pub const fn current_cursor(&self) -> DeliverySeq {
1510        self.current_cursor
1511    }
1512
1513    /// Fixed regression reason.
1514    #[must_use]
1515    pub const fn reason(&self) -> AckRegressionReason {
1516        let _ = self;
1517        AckRegressionReason::BelowCursor
1518    }
1519}
1520
1521/// Permanent terminal Leave result.
1522#[derive(Clone, Debug, PartialEq, Eq)]
1523pub struct LeaveCommitted {
1524    /// Conversation from the request.
1525    conversation_id: ConversationId,
1526    /// Committed Leave token.
1527    leave_attempt_token: LeaveAttemptToken,
1528    /// Retired participant.
1529    participant_id: ParticipantId,
1530    /// Permanent retired generation.
1531    retired_generation: Generation,
1532    /// Active binding ended by this same commit, if any.
1533    ended_binding_epoch: Option<BindingEpoch>,
1534    /// Earlier binding-terminal record, if one exists.
1535    prior_terminal_delivery_seq: Option<DeliverySeq>,
1536    /// Assigned Left delivery sequence.
1537    left_delivery_seq: DeliverySeq,
1538}
1539
1540impl LeaveCommitted {
1541    /// Constructs a terminal Leave outcome from its authoritative durable
1542    /// values.
1543    ///
1544    /// Returns `None` when a supplied active binding carries another
1545    /// generation or when a prior terminal is not strictly before `Left`.
1546    #[must_use]
1547    #[allow(clippy::too_many_arguments)]
1548    pub const fn new(
1549        conversation_id: ConversationId,
1550        leave_attempt_token: LeaveAttemptToken,
1551        participant_id: ParticipantId,
1552        retired_generation: Generation,
1553        ended_binding_epoch: Option<BindingEpoch>,
1554        prior_terminal_delivery_seq: Option<DeliverySeq>,
1555        left_delivery_seq: DeliverySeq,
1556    ) -> Option<Self> {
1557        if let Some(epoch) = ended_binding_epoch
1558            && epoch.capability_generation.get() != retired_generation.get()
1559        {
1560            return None;
1561        }
1562        if let Some(prior) = prior_terminal_delivery_seq
1563            && prior >= left_delivery_seq
1564        {
1565            return None;
1566        }
1567        Some(Self {
1568            conversation_id,
1569            leave_attempt_token,
1570            participant_id,
1571            retired_generation,
1572            ended_binding_epoch,
1573            prior_terminal_delivery_seq,
1574            left_delivery_seq,
1575        })
1576    }
1577
1578    /// Conversation from the request.
1579    #[must_use]
1580    pub const fn conversation_id(&self) -> ConversationId {
1581        self.conversation_id
1582    }
1583
1584    /// Committed Leave token.
1585    #[must_use]
1586    pub const fn leave_attempt_token(&self) -> LeaveAttemptToken {
1587        self.leave_attempt_token
1588    }
1589
1590    /// Retired participant.
1591    #[must_use]
1592    pub const fn participant_id(&self) -> ParticipantId {
1593        self.participant_id
1594    }
1595
1596    /// Presented generation, equal to the permanent retired generation.
1597    #[must_use]
1598    pub const fn presented_generation(&self) -> Generation {
1599        self.retired_generation
1600    }
1601
1602    /// Permanent retired generation.
1603    #[must_use]
1604    pub const fn retired_generation(&self) -> Generation {
1605        self.retired_generation
1606    }
1607
1608    /// Active binding ended by this same commit, if any.
1609    #[must_use]
1610    pub const fn ended_binding_epoch(&self) -> Option<BindingEpoch> {
1611        self.ended_binding_epoch
1612    }
1613
1614    /// Earlier binding-terminal record, if one exists.
1615    #[must_use]
1616    pub const fn prior_terminal_delivery_seq(&self) -> Option<DeliverySeq> {
1617        self.prior_terminal_delivery_seq
1618    }
1619
1620    /// Assigned Left delivery sequence.
1621    #[must_use]
1622    pub const fn left_delivery_seq(&self) -> DeliverySeq {
1623        self.left_delivery_seq
1624    }
1625}
1626
1627/// Marker acknowledgement advanced the cursor.
1628#[derive(Clone, Debug, PartialEq, Eq)]
1629pub struct MarkerAckCommitted {
1630    /// Marker-ack request envelope.
1631    request: MarkerAckEnvelope,
1632}
1633
1634impl MarkerAckCommitted {
1635    /// Constructs a committed marker acknowledgement whose cursor is the
1636    /// requested marker.
1637    #[must_use]
1638    pub const fn new(request: MarkerAckEnvelope) -> Self {
1639        Self { request }
1640    }
1641
1642    /// Marker-ack request envelope.
1643    #[must_use]
1644    pub const fn request(&self) -> &MarkerAckEnvelope {
1645        &self.request
1646    }
1647
1648    /// Resulting marker cursor, equal to `request.marker_delivery_seq`.
1649    #[must_use]
1650    pub const fn current_cursor(&self) -> DeliverySeq {
1651        self.request.marker_delivery_seq
1652    }
1653}
1654
1655/// Ordinary record commit result.
1656#[derive(Clone, Debug, PartialEq, Eq)]
1657pub struct RecordCommitted {
1658    /// Request envelope, without opaque payload.
1659    request: RecordAdmissionEnvelope,
1660    /// Assigned record sequence.
1661    delivery_seq: DeliverySeq,
1662}
1663
1664impl RecordCommitted {
1665    /// Constructs an ordinary commit and derives its verified sender from the
1666    /// authoritative request envelope.
1667    #[must_use]
1668    pub const fn new(request: RecordAdmissionEnvelope, delivery_seq: DeliverySeq) -> Self {
1669        Self {
1670            request,
1671            delivery_seq,
1672        }
1673    }
1674
1675    /// Request envelope, without opaque payload.
1676    #[must_use]
1677    pub const fn request(&self) -> &RecordAdmissionEnvelope {
1678        &self.request
1679    }
1680
1681    /// Verified sender, exactly the request participant.
1682    #[must_use]
1683    pub const fn sender_participant_id(&self) -> ParticipantId {
1684        self.request.participant_id
1685    }
1686
1687    /// Assigned record sequence.
1688    #[must_use]
1689    pub const fn delivery_seq(&self) -> DeliverySeq {
1690        self.delivery_seq
1691    }
1692}
1693
1694/// Ordinary record exceeds configured entry or byte maximum.
1695#[derive(Clone, Debug, PartialEq, Eq)]
1696pub struct RecordTooLarge {
1697    /// Request envelope, without opaque payload.
1698    pub request: RecordAdmissionEnvelope,
1699    /// First failing component.
1700    pub dimension: ResourceDimension,
1701    /// Exact durable record charge.
1702    pub encoded_record_charge: ResourceVector,
1703    /// Configured maximum ordinary record charge.
1704    pub max_ordinary_record_charge: ResourceVector,
1705}
1706
1707/// One observer progress status returned in request order.
1708#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1709pub struct ObserverProgressStatus {
1710    /// Conversation from the request entry.
1711    pub conversation_id: ConversationId,
1712    /// Presented refusal epoch.
1713    pub refused_epoch: ObserverEpoch,
1714    /// Current observer progress.
1715    pub current_observer_progress: DeliverySeq,
1716    /// Whether an equal epoch was atomically armed.
1717    pub armed: bool,
1718    /// Whether the presented epoch was already older/progressed.
1719    pub progressed: bool,
1720}
1721
1722/// Whole-batch observer recovery success.
1723#[derive(Clone, Debug, PartialEq, Eq)]
1724pub struct ObserverRecoveryAccepted {
1725    /// Request-ordered statuses. Wire uses one structural `u64` count only.
1726    pub statuses: Vec<ObserverProgressStatus>,
1727}
1728
1729/// Whole-batch invalid observer epoch.
1730#[derive(Clone, Debug, PartialEq, Eq)]
1731pub enum InvalidObserverEpoch {
1732    /// Conversation does not exist; current progress option is encoded `None`.
1733    ConversationUnknown {
1734        /// Unknown conversation.
1735        conversation_id: ConversationId,
1736        /// Presented epoch.
1737        presented_epoch: ObserverEpoch,
1738    },
1739    /// Presented epoch is ahead; current progress option is encoded `Some`.
1740    EpochAhead {
1741        /// Known conversation.
1742        conversation_id: ConversationId,
1743        /// Presented newer epoch.
1744        presented_epoch: ObserverEpoch,
1745        /// Current observer progress.
1746        current_observer_progress: DeliverySeq,
1747    },
1748}
1749
1750impl InvalidObserverEpoch {
1751    /// Returns the exact scalar reason tag implied by the selected body.
1752    #[must_use]
1753    pub const fn reason(&self) -> InvalidObserverEpochReason {
1754        match self {
1755            Self::ConversationUnknown { .. } => InvalidObserverEpochReason::ConversationUnknown,
1756            Self::EpochAhead { .. } => InvalidObserverEpochReason::EpochAhead,
1757        }
1758    }
1759}
1760
1761/// Whole-batch invalid observer recovery list.
1762#[derive(Clone, Debug, PartialEq, Eq)]
1763pub enum InvalidObserverEpochList {
1764    /// Request exceeds its signed entry limit.
1765    TooManyEntries {
1766        /// Presented list length.
1767        presented_entries: u64,
1768        /// Signed maximum entries.
1769        max_entries: u64,
1770    },
1771    /// Request repeats a conversation.
1772    DuplicateConversation {
1773        /// Repeated conversation.
1774        conversation_id: ConversationId,
1775        /// First request index.
1776        first_index: u64,
1777        /// Repeated request index.
1778        duplicate_index: u64,
1779    },
1780}
1781
1782impl InvalidObserverEpochList {
1783    /// Returns the exact scalar reason tag implied by the selected body.
1784    #[must_use]
1785    pub const fn reason(&self) -> InvalidObserverEpochListReason {
1786        match self {
1787            Self::TooManyEntries { .. } => InvalidObserverEpochListReason::TooManyEntries,
1788            Self::DuplicateConversation { .. } => {
1789                InvalidObserverEpochListReason::DuplicateConversation
1790            }
1791        }
1792    }
1793}
1794
1795/// Exhaustive server-to-client semantic participant value.
1796#[derive(Clone, Debug, PartialEq, Eq)]
1797pub enum ServerValue {
1798    /// `0x0100`.
1799    ParticipantTransportRejected(ParticipantTransportRejected),
1800    /// `0x0101`.
1801    AttemptTokenBodyConflict(AttemptTokenBodyConflict),
1802    /// `0x0102` or `0x0124`, selected by the inner exact schema.
1803    ConnectionConversationCapacityExceeded(ConnectionConversationCapacityExceeded),
1804    /// `0x0103`.
1805    ConnectionConversationBindingOccupied(ConnectionConversationBindingOccupied),
1806    /// `0x0104`.
1807    ConversationOrderExhausted(Box<ConversationOrderExhausted>),
1808    /// `0x0105`.
1809    ParticipantUnknown(ParticipantUnknown),
1810    /// `0x0106`.
1811    NoBinding(NoBinding),
1812    /// `0x0107`.
1813    StaleAuthority(StaleAuthority),
1814    /// `0x0108`.
1815    Retired(Retired),
1816    /// `0x0109`.
1817    MarkerClosureCapacityExceeded(Box<MarkerClosureCapacityExceeded>),
1818    /// `0x010A`.
1819    EnrollBound(EnrollBound),
1820    /// `0x010B`.
1821    EnrollmentKnown(EnrollmentKnown),
1822    /// `0x010C`.
1823    ReceiptExpired(ReceiptExpired),
1824    /// `0x010D`.
1825    ReceiptCapacityExceeded(ReceiptCapacityExceeded),
1826    /// `0x010E`.
1827    IdentityCapacityExceeded(IdentityCapacityExceeded),
1828    /// `0x010F`.
1829    ObserverBackpressure(ObserverBackpressure),
1830    /// `0x0110`.
1831    ConversationSequenceExhausted(Box<ConversationSequenceExhausted>),
1832    /// `0x0111`.
1833    AttachBound(AttachBound),
1834    /// `0x0112`.
1835    StaleOrUnknownReceipt(StaleOrUnknownReceipt),
1836    /// `0x0113`.
1837    MarkerNotDelivered(MarkerNotDelivered),
1838    /// `0x0114`.
1839    MarkerMismatch(MarkerMismatch),
1840    /// `0x0115`.
1841    Bound(ReceiptReplay),
1842    /// `0x0116`.
1843    UnboundReceipt(ReceiptReplay),
1844    /// `0x0117`.
1845    DetachCommitted(DetachCommitted),
1846    /// `0x0118`.
1847    DetachInProgress(DetachInProgress),
1848    /// `0x0119`.
1849    AckCommitted(AckCommitted),
1850    /// `0x011A`.
1851    AckNoOp(AckNoOp),
1852    /// `0x011B`.
1853    AckGap(AckGap),
1854    /// `0x011C`.
1855    AckRegression(AckRegression),
1856    /// `0x011D`.
1857    LeaveCommitted(LeaveCommitted),
1858    /// `0x011E`.
1859    MarkerAckCommitted(MarkerAckCommitted),
1860    /// `0x011F`.
1861    RecordCommitted(RecordCommitted),
1862    /// `0x0120`.
1863    RecordTooLarge(RecordTooLarge),
1864    /// `0x0121`.
1865    ObserverRecoveryAccepted(ObserverRecoveryAccepted),
1866    /// `0x0122`.
1867    InvalidObserverEpoch(InvalidObserverEpoch),
1868    /// `0x0123`.
1869    InvalidObserverEpochList(InvalidObserverEpochList),
1870    /// `0x0125`.
1871    MarkerSettlementBackpressure(MarkerSettlementBackpressure),
1872    /// `0x0126`.
1873    EnrollmentSettlementBackpressure(EnrollmentSettlementBackpressure),
1874}
1875
1876impl ServerValue {
1877    /// Returns the exact contiguous server value discriminant.
1878    #[must_use]
1879    pub const fn discriminant(&self) -> ServerDiscriminant {
1880        match self {
1881            Self::ParticipantTransportRejected(_) => {
1882                ServerDiscriminant::ParticipantTransportRejected
1883            }
1884            Self::AttemptTokenBodyConflict(_) => ServerDiscriminant::AttemptTokenBodyConflict,
1885            Self::ConnectionConversationCapacityExceeded(value) => match value {
1886                ConnectionConversationCapacityExceeded::SemanticRequest { .. } => {
1887                    ServerDiscriminant::ConnectionConversationCapacityExceeded
1888                }
1889                ConnectionConversationCapacityExceeded::ObserverRecovery { .. } => {
1890                    ServerDiscriminant::ObserverRecoveryConnectionCapacityExceeded
1891                }
1892            },
1893            Self::ConnectionConversationBindingOccupied(_) => {
1894                ServerDiscriminant::ConnectionConversationBindingOccupied
1895            }
1896            Self::ConversationOrderExhausted(_) => ServerDiscriminant::ConversationOrderExhausted,
1897            Self::ParticipantUnknown(_) => ServerDiscriminant::ParticipantUnknown,
1898            Self::NoBinding(_) => ServerDiscriminant::NoBinding,
1899            Self::StaleAuthority(_) => ServerDiscriminant::StaleAuthority,
1900            Self::Retired(_) => ServerDiscriminant::Retired,
1901            Self::MarkerClosureCapacityExceeded(_) => {
1902                ServerDiscriminant::MarkerClosureCapacityExceeded
1903            }
1904            Self::EnrollBound(_) => ServerDiscriminant::EnrollBound,
1905            Self::EnrollmentKnown(_) => ServerDiscriminant::EnrollmentKnown,
1906            Self::ReceiptExpired(_) => ServerDiscriminant::ReceiptExpired,
1907            Self::ReceiptCapacityExceeded(_) => ServerDiscriminant::ReceiptCapacityExceeded,
1908            Self::IdentityCapacityExceeded(_) => ServerDiscriminant::IdentityCapacityExceeded,
1909            Self::ObserverBackpressure(_) => ServerDiscriminant::ObserverBackpressure,
1910            Self::ConversationSequenceExhausted(_) => {
1911                ServerDiscriminant::ConversationSequenceExhausted
1912            }
1913            Self::AttachBound(_) => ServerDiscriminant::AttachBound,
1914            Self::StaleOrUnknownReceipt(_) => ServerDiscriminant::StaleOrUnknownReceipt,
1915            Self::MarkerNotDelivered(_) => ServerDiscriminant::MarkerNotDelivered,
1916            Self::MarkerMismatch(_) => ServerDiscriminant::MarkerMismatch,
1917            Self::Bound(_) => ServerDiscriminant::Bound,
1918            Self::UnboundReceipt(_) => ServerDiscriminant::UnboundReceipt,
1919            Self::DetachCommitted(_) => ServerDiscriminant::DetachCommitted,
1920            Self::DetachInProgress(_) => ServerDiscriminant::DetachInProgress,
1921            Self::AckCommitted(_) => ServerDiscriminant::AckCommitted,
1922            Self::AckNoOp(_) => ServerDiscriminant::AckNoOp,
1923            Self::AckGap(_) => ServerDiscriminant::AckGap,
1924            Self::AckRegression(_) => ServerDiscriminant::AckRegression,
1925            Self::LeaveCommitted(_) => ServerDiscriminant::LeaveCommitted,
1926            Self::MarkerAckCommitted(_) => ServerDiscriminant::MarkerAckCommitted,
1927            Self::RecordCommitted(_) => ServerDiscriminant::RecordCommitted,
1928            Self::RecordTooLarge(_) => ServerDiscriminant::RecordTooLarge,
1929            Self::ObserverRecoveryAccepted(_) => ServerDiscriminant::ObserverRecoveryAccepted,
1930            Self::InvalidObserverEpoch(_) => ServerDiscriminant::InvalidObserverEpoch,
1931            Self::InvalidObserverEpochList(_) => ServerDiscriminant::InvalidObserverEpochList,
1932            Self::MarkerSettlementBackpressure(_) => {
1933                ServerDiscriminant::MarkerSettlementBackpressure
1934            }
1935            Self::EnrollmentSettlementBackpressure(_) => {
1936                ServerDiscriminant::EnrollmentSettlementBackpressure
1937            }
1938        }
1939    }
1940
1941    /// Returns the structural originating-request selector when the value has one.
1942    #[must_use]
1943    #[allow(clippy::too_many_lines)]
1944    pub const fn originating_request(&self) -> Option<ClientDiscriminant> {
1945        match self {
1946            Self::ParticipantTransportRejected(_)
1947            | Self::ObserverRecoveryAccepted(_)
1948            | Self::InvalidObserverEpoch(_)
1949            | Self::InvalidObserverEpochList(_) => None,
1950            Self::AttemptTokenBodyConflict(value) => Some(match value {
1951                AttemptTokenBodyConflict::CredentialAttach { .. } => {
1952                    ClientDiscriminant::CredentialAttachRequest
1953                }
1954                AttemptTokenBodyConflict::Leave { .. } => ClientDiscriminant::LeaveRequest,
1955                AttemptTokenBodyConflict::RecordAdmission { .. } => {
1956                    ClientDiscriminant::RecordAdmission
1957                }
1958            }),
1959            Self::ConnectionConversationCapacityExceeded(value) => match value {
1960                ConnectionConversationCapacityExceeded::SemanticRequest { request, .. } => {
1961                    Some(request.originating_request())
1962                }
1963                ConnectionConversationCapacityExceeded::ObserverRecovery { .. } => None,
1964            },
1965            Self::ConnectionConversationBindingOccupied(value) => Some(match value {
1966                ConnectionConversationBindingOccupied::Enrollment { .. } => {
1967                    ClientDiscriminant::EnrollmentRequest
1968                }
1969                ConnectionConversationBindingOccupied::CredentialAttach { .. } => {
1970                    ClientDiscriminant::CredentialAttachRequest
1971                }
1972            }),
1973            Self::ConversationOrderExhausted(value) => Some(match value.request() {
1974                OrderAllocatingEnvelope::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
1975                OrderAllocatingEnvelope::CredentialAttach(_) => {
1976                    ClientDiscriminant::CredentialAttachRequest
1977                }
1978                OrderAllocatingEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
1979            }),
1980            Self::ParticipantUnknown(value) => Some(participant_reference_origin(&value.request)),
1981            Self::NoBinding(value) => Some(binding_required_origin(&value.request)),
1982            Self::StaleAuthority(value) => Some(stale_authority_origin(value)),
1983            Self::Retired(value) => Some(match value {
1984                Retired::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
1985                Retired::Participant { request, .. } => participant_reference_origin(request),
1986            }),
1987            Self::MarkerClosureCapacityExceeded(value) => Some(match &value.request {
1988                ClosureCheckedEnvelope::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
1989                ClosureCheckedEnvelope::CredentialAttach(_) => {
1990                    ClientDiscriminant::CredentialAttachRequest
1991                }
1992                ClosureCheckedEnvelope::Leave(_) => ClientDiscriminant::LeaveRequest,
1993                ClosureCheckedEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
1994            }),
1995            Self::EnrollBound(_)
1996            | Self::EnrollmentKnown(_)
1997            | Self::IdentityCapacityExceeded(_)
1998            | Self::EnrollmentSettlementBackpressure(_) => {
1999                Some(ClientDiscriminant::EnrollmentRequest)
2000            }
2001            Self::ReceiptExpired(value) => Some(match value {
2002                ReceiptExpired::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2003                ReceiptExpired::CredentialAttach { .. } => {
2004                    ClientDiscriminant::CredentialAttachRequest
2005                }
2006            }),
2007            Self::ReceiptCapacityExceeded(value) => Some(match value {
2008                ReceiptCapacityExceeded::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2009                ReceiptCapacityExceeded::CredentialAttach { .. } => {
2010                    ClientDiscriminant::CredentialAttachRequest
2011                }
2012            }),
2013            Self::ObserverBackpressure(value) => Some(match value {
2014                ObserverBackpressure::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2015                ObserverBackpressure::CredentialAttach { .. } => {
2016                    ClientDiscriminant::CredentialAttachRequest
2017                }
2018                ObserverBackpressure::Detach { .. } => ClientDiscriminant::DetachRequest,
2019                ObserverBackpressure::Leave { .. } => ClientDiscriminant::LeaveRequest,
2020                ObserverBackpressure::RecordAdmission { .. } => ClientDiscriminant::RecordAdmission,
2021            }),
2022            Self::ConversationSequenceExhausted(value) => Some(match &value.request {
2023                SequenceAllocatingEnvelope::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
2024                SequenceAllocatingEnvelope::CredentialAttach(_) => {
2025                    ClientDiscriminant::CredentialAttachRequest
2026                }
2027                SequenceAllocatingEnvelope::RecordAdmission(_) => {
2028                    ClientDiscriminant::RecordAdmission
2029                }
2030            }),
2031            Self::AttachBound(_) | Self::StaleOrUnknownReceipt(_) => {
2032                Some(ClientDiscriminant::CredentialAttachRequest)
2033            }
2034            Self::MarkerNotDelivered(value) => Some(marker_proof_origin(&value.request)),
2035            Self::MarkerMismatch(value) => Some(marker_proof_origin(&value.request)),
2036            Self::Bound(value) | Self::UnboundReceipt(value) => Some(match value {
2037                ReceiptReplay::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
2038                ReceiptReplay::CredentialAttach(_) => ClientDiscriminant::CredentialAttachRequest,
2039            }),
2040            Self::DetachCommitted(_) | Self::DetachInProgress(_) => {
2041                Some(ClientDiscriminant::DetachRequest)
2042            }
2043            Self::AckCommitted(_) | Self::AckGap(_) | Self::AckRegression(_) => {
2044                Some(ClientDiscriminant::ParticipantAck)
2045            }
2046            Self::AckNoOp(value) => Some(match value {
2047                AckNoOp::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2048                AckNoOp::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2049            }),
2050            Self::LeaveCommitted(_) => Some(ClientDiscriminant::LeaveRequest),
2051            Self::MarkerAckCommitted(_) => Some(ClientDiscriminant::MarkerAck),
2052            Self::RecordCommitted(_) | Self::RecordTooLarge(_) => {
2053                Some(ClientDiscriminant::RecordAdmission)
2054            }
2055            Self::MarkerSettlementBackpressure(value) => Some(match value {
2056                MarkerSettlementBackpressure::CredentialAttach { .. } => {
2057                    ClientDiscriminant::CredentialAttachRequest
2058                }
2059                MarkerSettlementBackpressure::Detach { .. } => ClientDiscriminant::DetachRequest,
2060            }),
2061        }
2062    }
2063}
2064
2065const fn participant_reference_origin(
2066    request: &ParticipantReferenceEnvelope,
2067) -> ClientDiscriminant {
2068    match request {
2069        ParticipantReferenceEnvelope::CredentialAttach(_) => {
2070            ClientDiscriminant::CredentialAttachRequest
2071        }
2072        ParticipantReferenceEnvelope::Detach(_) => ClientDiscriminant::DetachRequest,
2073        ParticipantReferenceEnvelope::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2074        ParticipantReferenceEnvelope::Leave(_) => ClientDiscriminant::LeaveRequest,
2075        ParticipantReferenceEnvelope::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2076        ParticipantReferenceEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2077    }
2078}
2079
2080const fn binding_required_origin(request: &BindingRequiredEnvelope) -> ClientDiscriminant {
2081    match request {
2082        BindingRequiredEnvelope::Detach(_) => ClientDiscriminant::DetachRequest,
2083        BindingRequiredEnvelope::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2084        BindingRequiredEnvelope::Leave(_) => ClientDiscriminant::LeaveRequest,
2085        BindingRequiredEnvelope::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2086        BindingRequiredEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2087    }
2088}
2089
2090const fn stale_authority_origin(value: &StaleAuthority) -> ClientDiscriminant {
2091    match value {
2092        StaleAuthority::Live { request, .. } => match request {
2093            CommonStaleAuthorityEnvelope::CredentialAttach(_) => {
2094                ClientDiscriminant::CredentialAttachRequest
2095            }
2096            CommonStaleAuthorityEnvelope::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2097            CommonStaleAuthorityEnvelope::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2098            CommonStaleAuthorityEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2099        },
2100        StaleAuthority::Detach(_) => ClientDiscriminant::DetachRequest,
2101        StaleAuthority::Leave(_) => ClientDiscriminant::LeaveRequest,
2102    }
2103}
2104
2105const fn marker_proof_origin(request: &MarkerProofRequest) -> ClientDiscriminant {
2106    match request {
2107        MarkerProofRequest::CredentialAttach(_) => ClientDiscriminant::CredentialAttachRequest,
2108        MarkerProofRequest::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2109    }
2110}
2111
2112/// Exact capability string serialized by transport capability refusal.
2113pub const PARTICIPANT_CAPABILITY: &str = "participant-v1";
2114
2115/// Returns the exact attempt operation implied by a body-conflict variant.
2116#[must_use]
2117pub const fn attempt_operation(value: &AttemptTokenBodyConflict) -> super::AttemptOperation {
2118    match value {
2119        AttemptTokenBodyConflict::CredentialAttach { .. } => {
2120            super::AttemptOperation::CredentialAttachRequest
2121        }
2122        AttemptTokenBodyConflict::Leave { .. } => super::AttemptOperation::LeaveRequest,
2123        AttemptTokenBodyConflict::RecordAdmission { .. } => {
2124            super::AttemptOperation::RecordAdmission
2125        }
2126    }
2127}
2128
2129/// Owns a capability string after wire decoding while retaining domain validation.
2130#[must_use]
2131pub fn capability_string() -> String {
2132    String::from(PARTICIPANT_CAPABILITY)
2133}