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