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