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/// A token-carrying request whose durable write the server could not settle
1057/// (`0x0128`).
1058///
1059/// The store answered the request's write with an unknown outcome: it may
1060/// have been applied. The server read the position back and could not say,
1061/// so it names that here instead of closing the connection with no frame.
1062/// The connection stays open and the conversation is rebuilt from the store
1063/// on its next touch. The request is not answered: the client re-presents it
1064/// with the same attempt token, and is answered its original commit if the
1065/// write landed, or the one commit it then makes if it did not.
1066///
1067/// It is answered inside participant-v1, as `0x0127` was: the participant
1068/// inner version is compared for equality, with no negotiation, so a bump
1069/// would refuse every frame. A v1.0 decoder meets this discriminant as
1070/// `UnknownDiscriminant`, a connection fault, keeps its slot and re-presents.
1071#[derive(Clone, Debug, PartialEq, Eq)]
1072pub enum WriteOutcomeUnknown {
1073    /// Enrollment.
1074    Enrollment {
1075        /// Request envelope.
1076        request: EnrollmentEnvelope,
1077    },
1078    /// Credential attach.
1079    CredentialAttach {
1080        /// Request envelope.
1081        request: AttachEnvelope,
1082    },
1083    /// Detach.
1084    Detach {
1085        /// Request envelope.
1086        request: DetachEnvelope,
1087    },
1088    /// Leave.
1089    Leave {
1090        /// Request envelope.
1091        request: LeaveEnvelope,
1092    },
1093    /// Ordinary admission.
1094    RecordAdmission {
1095        /// Request envelope.
1096        request: RecordAdmissionEnvelope,
1097    },
1098}
1099
1100/// Request alternatives that can exhaust optional sequence admission.
1101#[derive(Clone, Debug, PartialEq, Eq)]
1102pub enum SequenceAllocatingEnvelope {
1103    /// Enrollment.
1104    Enrollment(EnrollmentEnvelope),
1105    /// Credential attach.
1106    CredentialAttach(AttachEnvelope),
1107    /// Ordinary record admission.
1108    RecordAdmission(RecordAdmissionEnvelope),
1109}
1110
1111/// Canonical sequence-exhaustion response.
1112#[derive(Clone, Debug, PartialEq, Eq)]
1113pub struct ConversationSequenceExhausted {
1114    /// Exact triggering request envelope.
1115    pub request: SequenceAllocatingEnvelope,
1116    /// Exactly one canonical ten-field budget.
1117    pub sequence_budget: SequenceBudget,
1118}
1119
1120/// Canonical successful credential-attach receipt.
1121#[derive(Clone, Debug, PartialEq, Eq)]
1122pub struct AttachBound {
1123    /// Conversation from the request.
1124    conversation_id: ConversationId,
1125    /// Attach token echoed as the result token.
1126    token: AttachAttemptToken,
1127    /// Participant from the request.
1128    participant_id: ParticipantId,
1129    /// Originally presented generation.
1130    request_generation: Generation,
1131    /// Newly minted attach secret.
1132    attach_secret: AttachSecret,
1133    /// Origin binding epoch.
1134    origin_binding_epoch: BindingEpoch,
1135    /// Persisted participant cursor.
1136    persisted_cursor: DeliverySeq,
1137    /// Marker accepted atomically by recovery.
1138    accepted_marker_delivery_seq: Option<DeliverySeq>,
1139    /// Receipt deadline.
1140    receipt_expires_at: u128,
1141    /// Provenance deadline.
1142    provenance_expires_at: u128,
1143}
1144
1145impl AttachBound {
1146    /// Constructs an ordinary attach receipt.
1147    ///
1148    /// Returns `None` unless the origin epoch carries the exact checked
1149    /// successor of `request_generation`. Ordinary attach structurally records
1150    /// no accepted marker and preserves the supplied cursor.
1151    #[must_use]
1152    #[allow(clippy::too_many_arguments)]
1153    pub const fn ordinary(
1154        conversation_id: ConversationId,
1155        token: AttachAttemptToken,
1156        participant_id: ParticipantId,
1157        request_generation: Generation,
1158        attach_secret: AttachSecret,
1159        origin_binding_epoch: BindingEpoch,
1160        persisted_cursor: DeliverySeq,
1161        receipt_expires_at: u128,
1162        provenance_expires_at: u128,
1163    ) -> Option<Self> {
1164        if !is_successor_generation(
1165            request_generation,
1166            origin_binding_epoch.capability_generation,
1167        ) {
1168            return None;
1169        }
1170        Some(Self {
1171            conversation_id,
1172            token,
1173            participant_id,
1174            request_generation,
1175            attach_secret,
1176            origin_binding_epoch,
1177            persisted_cursor,
1178            accepted_marker_delivery_seq: None,
1179            receipt_expires_at,
1180            provenance_expires_at,
1181        })
1182    }
1183
1184    /// Constructs a fenced-recovery attach receipt.
1185    ///
1186    /// Returns `None` unless the origin epoch carries the exact checked
1187    /// successor of `request_generation`. The accepted marker is also the
1188    /// resulting persisted cursor by construction.
1189    #[must_use]
1190    #[allow(clippy::too_many_arguments)]
1191    pub const fn fenced(
1192        conversation_id: ConversationId,
1193        token: AttachAttemptToken,
1194        participant_id: ParticipantId,
1195        request_generation: Generation,
1196        attach_secret: AttachSecret,
1197        origin_binding_epoch: BindingEpoch,
1198        accepted_marker_delivery_seq: DeliverySeq,
1199        receipt_expires_at: u128,
1200        provenance_expires_at: u128,
1201    ) -> Option<Self> {
1202        if !is_successor_generation(
1203            request_generation,
1204            origin_binding_epoch.capability_generation,
1205        ) {
1206            return None;
1207        }
1208        Some(Self {
1209            conversation_id,
1210            token,
1211            participant_id,
1212            request_generation,
1213            attach_secret,
1214            origin_binding_epoch,
1215            persisted_cursor: accepted_marker_delivery_seq,
1216            accepted_marker_delivery_seq: Some(accepted_marker_delivery_seq),
1217            receipt_expires_at,
1218            provenance_expires_at,
1219        })
1220    }
1221
1222    /// Conversation from the request.
1223    #[must_use]
1224    pub const fn conversation_id(&self) -> ConversationId {
1225        self.conversation_id
1226    }
1227
1228    /// Attach token echoed as the result token.
1229    #[must_use]
1230    pub const fn token(&self) -> AttachAttemptToken {
1231        self.token
1232    }
1233
1234    /// Participant from the request.
1235    #[must_use]
1236    pub const fn participant_id(&self) -> ParticipantId {
1237        self.participant_id
1238    }
1239
1240    /// Originally presented generation.
1241    #[must_use]
1242    pub const fn request_generation(&self) -> Generation {
1243        self.request_generation
1244    }
1245
1246    /// Exact successor capability generation.
1247    #[must_use]
1248    pub const fn capability_generation(&self) -> Generation {
1249        self.origin_binding_epoch.capability_generation
1250    }
1251
1252    /// Newly minted attach secret.
1253    #[must_use]
1254    pub const fn attach_secret(&self) -> AttachSecret {
1255        self.attach_secret
1256    }
1257
1258    /// Origin binding epoch carrying the result generation.
1259    #[must_use]
1260    pub const fn origin_binding_epoch(&self) -> BindingEpoch {
1261        self.origin_binding_epoch
1262    }
1263
1264    /// Persisted participant cursor.
1265    #[must_use]
1266    pub const fn persisted_cursor(&self) -> DeliverySeq {
1267        self.persisted_cursor
1268    }
1269
1270    /// Marker accepted atomically by recovery, if this was the fenced path.
1271    #[must_use]
1272    pub const fn accepted_marker_delivery_seq(&self) -> Option<DeliverySeq> {
1273        self.accepted_marker_delivery_seq
1274    }
1275
1276    /// Receipt deadline.
1277    #[must_use]
1278    pub const fn receipt_expires_at(&self) -> u128 {
1279        self.receipt_expires_at
1280    }
1281
1282    /// Provenance deadline.
1283    #[must_use]
1284    pub const fn provenance_expires_at(&self) -> u128 {
1285        self.provenance_expires_at
1286    }
1287}
1288
1289const fn is_successor_generation(previous: Generation, successor: Generation) -> bool {
1290    match previous.get().checked_add(1) {
1291        Some(expected) => successor.get() == expected,
1292        None => false,
1293    }
1294}
1295
1296/// Attach receipt is no longer known after provenance expiry.
1297#[derive(Clone, Debug, PartialEq, Eq)]
1298pub struct StaleOrUnknownReceipt {
1299    /// Conversation from the request.
1300    pub conversation_id: ConversationId,
1301    /// Attach token.
1302    pub token: AttachAttemptToken,
1303    /// Participant from the request.
1304    pub participant_id: ParticipantId,
1305    /// Originally presented generation.
1306    pub presented_generation: Generation,
1307    /// Originally presented marker option.
1308    pub presented_marker_delivery_seq: Option<DeliverySeq>,
1309    /// Current live generation.
1310    pub current_generation: Generation,
1311}
1312
1313/// Attach marker-proof request fields; attach token is part of the replacement schema.
1314#[derive(Clone, Debug, PartialEq, Eq)]
1315pub struct AttachMarkerProof {
1316    /// Conversation from the request.
1317    pub conversation_id: ConversationId,
1318    /// Attach token from the request.
1319    pub token: AttachAttemptToken,
1320    /// Participant from the request.
1321    pub participant_id: ParticipantId,
1322    /// Presented generation.
1323    pub capability_generation: Generation,
1324    /// Explicit requested marker.
1325    pub requested_marker_delivery_seq: DeliverySeq,
1326}
1327
1328/// Marker-ack proof request fields.
1329#[derive(Clone, Debug, PartialEq, Eq)]
1330pub struct MarkerAckProof {
1331    /// Conversation from the request.
1332    pub conversation_id: ConversationId,
1333    /// Participant from the request.
1334    pub participant_id: ParticipantId,
1335    /// Presented generation.
1336    pub capability_generation: Generation,
1337    /// Explicit requested marker.
1338    pub requested_marker_delivery_seq: DeliverySeq,
1339}
1340
1341/// Marker-proof request alternatives.
1342#[derive(Clone, Debug, PartialEq, Eq)]
1343pub enum MarkerProofRequest {
1344    /// Credential attach proof.
1345    CredentialAttach(AttachMarkerProof),
1346    /// Marker acknowledgement proof.
1347    MarkerAck(MarkerAckProof),
1348}
1349
1350/// Requested marker was not delivered to the proof epoch.
1351#[derive(Clone, Debug, PartialEq, Eq)]
1352pub struct MarkerNotDelivered {
1353    /// Complete flattened request fields.
1354    pub request: MarkerProofRequest,
1355    /// Singleton reason tag.
1356    pub reason: MarkerNotDeliveredReason,
1357    /// Marker actually expected by current state.
1358    pub expected_marker_delivery_seq: DeliverySeq,
1359}
1360
1361/// Exact marker-mismatch reason body; no optional field bag exists.
1362#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1363pub enum MarkerMismatchBody {
1364    /// Requested marker is below current cursor.
1365    BelowCursor {
1366        /// Current participant cursor.
1367        current_cursor: DeliverySeq,
1368    },
1369    /// No marker is expected by current state.
1370    NoMarkerExpected,
1371    /// A different marker is expected.
1372    ExpectedDifferentMarker {
1373        /// Expected marker sequence.
1374        expected_marker_delivery_seq: DeliverySeq,
1375    },
1376}
1377
1378impl MarkerMismatchBody {
1379    /// Returns the stable reason selector.
1380    #[must_use]
1381    pub const fn reason(self) -> MarkerMismatchReason {
1382        match self {
1383            Self::BelowCursor { .. } => MarkerMismatchReason::BelowCursor,
1384            Self::NoMarkerExpected => MarkerMismatchReason::NoMarkerExpected,
1385            Self::ExpectedDifferentMarker { .. } => MarkerMismatchReason::ExpectedDifferentMarker,
1386        }
1387    }
1388}
1389
1390/// Presented marker does not match current marker state.
1391#[derive(Clone, Debug, PartialEq, Eq)]
1392pub struct MarkerMismatch {
1393    /// Complete flattened request fields.
1394    pub request: MarkerProofRequest,
1395    /// Selected exact reason body.
1396    pub mismatch: MarkerMismatchBody,
1397}
1398
1399/// Complete canonical receipt replay payload used by Bound/UnboundReceipt.
1400#[derive(Clone, Debug, PartialEq, Eq)]
1401pub enum ReceiptReplay {
1402    /// Enrollment canonical receipt; generation/cursor/marker constants are
1403    /// enforced by [`EnrollBound`].
1404    Enrollment(EnrollBound),
1405    /// Credential-attach canonical receipt; successor generation and
1406    /// cursor/marker relations are enforced by [`AttachBound`].
1407    CredentialAttach(AttachBound),
1408}
1409
1410/// Stable committed detach response.
1411#[derive(Clone, Debug, PartialEq, Eq)]
1412pub struct DetachCommitted {
1413    /// Conversation from the request.
1414    conversation_id: ConversationId,
1415    /// Participant from the request.
1416    participant_id: ParticipantId,
1417    /// Committed detach token.
1418    detach_attempt_token: DetachAttemptToken,
1419    /// Binding epoch ended by detach.
1420    committed_binding_epoch: BindingEpoch,
1421    /// Assigned Detached delivery sequence.
1422    detached_delivery_seq: DeliverySeq,
1423}
1424
1425impl DetachCommitted {
1426    /// Constructs a detach result and derives its presented generation from
1427    /// the binding epoch it ended.
1428    #[must_use]
1429    pub const fn new(
1430        conversation_id: ConversationId,
1431        participant_id: ParticipantId,
1432        detach_attempt_token: DetachAttemptToken,
1433        committed_binding_epoch: BindingEpoch,
1434        detached_delivery_seq: DeliverySeq,
1435    ) -> Self {
1436        Self {
1437            conversation_id,
1438            participant_id,
1439            detach_attempt_token,
1440            committed_binding_epoch,
1441            detached_delivery_seq,
1442        }
1443    }
1444
1445    /// Conversation from the request.
1446    #[must_use]
1447    pub const fn conversation_id(&self) -> ConversationId {
1448        self.conversation_id
1449    }
1450
1451    /// Participant from the request.
1452    #[must_use]
1453    pub const fn participant_id(&self) -> ParticipantId {
1454        self.participant_id
1455    }
1456
1457    /// Presented generation, equal to the committed binding epoch generation.
1458    #[must_use]
1459    pub const fn capability_generation(&self) -> Generation {
1460        self.committed_binding_epoch.capability_generation
1461    }
1462
1463    /// Committed detach token.
1464    #[must_use]
1465    pub const fn detach_attempt_token(&self) -> DetachAttemptToken {
1466        self.detach_attempt_token
1467    }
1468
1469    /// Binding epoch ended by detach.
1470    #[must_use]
1471    pub const fn committed_binding_epoch(&self) -> BindingEpoch {
1472        self.committed_binding_epoch
1473    }
1474
1475    /// Assigned Detached delivery sequence.
1476    #[must_use]
1477    pub const fn detached_delivery_seq(&self) -> DeliverySeq {
1478        self.detached_delivery_seq
1479    }
1480}
1481
1482/// Different detach token encountered an existing pending cell.
1483#[derive(Clone, Debug, PartialEq, Eq)]
1484pub struct DetachInProgress {
1485    /// Conversation from the competing request.
1486    pub conversation_id: ConversationId,
1487    /// Participant from the competing request.
1488    pub participant_id: ParticipantId,
1489    /// Competing presented token; stored token is never disclosed.
1490    pub presented_token: DetachAttemptToken,
1491    /// Competing presented generation.
1492    pub presented_generation: Generation,
1493    /// Binding epoch being terminalized by the pending cell.
1494    pub committed_binding_epoch: BindingEpoch,
1495}
1496
1497/// Continuous acknowledgement advanced the cursor.
1498#[derive(Clone, Debug, PartialEq, Eq)]
1499pub struct AckCommitted {
1500    /// Request envelope.
1501    request: ParticipantAckEnvelope,
1502}
1503
1504impl AckCommitted {
1505    /// Constructs a committed acknowledgement whose cursor is the requested
1506    /// cumulative boundary.
1507    #[must_use]
1508    pub const fn new(request: ParticipantAckEnvelope) -> Self {
1509        Self { request }
1510    }
1511
1512    /// Request envelope.
1513    #[must_use]
1514    pub const fn request(&self) -> &ParticipantAckEnvelope {
1515        &self.request
1516    }
1517
1518    /// Resulting committed cursor, equal to `request.through_seq`.
1519    #[must_use]
1520    pub const fn current_cursor(&self) -> DeliverySeq {
1521        self.request.through_seq
1522    }
1523}
1524
1525/// Idempotent normal or marker acknowledgement.
1526#[derive(Clone, Debug, PartialEq, Eq)]
1527pub enum AckNoOp {
1528    /// Continuous acknowledgement.
1529    ParticipantAck(ParticipantAckEnvelope),
1530    /// Marker acknowledgement.
1531    MarkerAck(MarkerAckEnvelope),
1532}
1533
1534impl AckNoOp {
1535    /// Constructs an idempotent continuous acknowledgement at its requested
1536    /// cursor.
1537    #[must_use]
1538    pub const fn participant_ack(request: ParticipantAckEnvelope) -> Self {
1539        Self::ParticipantAck(request)
1540    }
1541
1542    /// Constructs an idempotent marker acknowledgement at its requested
1543    /// marker cursor.
1544    #[must_use]
1545    pub const fn marker_ack(request: MarkerAckEnvelope) -> Self {
1546        Self::MarkerAck(request)
1547    }
1548
1549    /// Unchanged cursor, derived from the selected request envelope.
1550    #[must_use]
1551    pub const fn current_cursor(&self) -> DeliverySeq {
1552        match self {
1553            Self::ParticipantAck(request) => request.through_seq,
1554            Self::MarkerAck(request) => request.marker_delivery_seq,
1555        }
1556    }
1557}
1558
1559/// Continuous acknowledgement crossed a gap.
1560#[derive(Clone, Debug, PartialEq, Eq)]
1561pub struct AckGap {
1562    /// Request envelope.
1563    request: ParticipantAckEnvelope,
1564    /// Unchanged cursor.
1565    current_cursor: DeliverySeq,
1566}
1567
1568impl AckGap {
1569    /// Constructs a gap refusal for a requested boundary above the unchanged
1570    /// 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 gap reason.
1596    #[must_use]
1597    pub const fn reason(&self) -> AckGapReason {
1598        let _ = self;
1599        AckGapReason::NotContiguouslyAvailable
1600    }
1601}
1602
1603/// Continuous acknowledgement regressed below the cursor.
1604#[derive(Clone, Debug, PartialEq, Eq)]
1605pub struct AckRegression {
1606    /// Request envelope.
1607    request: ParticipantAckEnvelope,
1608    /// Unchanged cursor.
1609    current_cursor: DeliverySeq,
1610}
1611
1612impl AckRegression {
1613    /// Constructs a regression refusal for a requested boundary below the
1614    /// unchanged cursor.
1615    #[must_use]
1616    pub const fn new(request: ParticipantAckEnvelope, current_cursor: DeliverySeq) -> Option<Self> {
1617        if request.through_seq < current_cursor {
1618            Some(Self {
1619                request,
1620                current_cursor,
1621            })
1622        } else {
1623            None
1624        }
1625    }
1626
1627    /// Request envelope.
1628    #[must_use]
1629    pub const fn request(&self) -> &ParticipantAckEnvelope {
1630        &self.request
1631    }
1632
1633    /// Unchanged cursor.
1634    #[must_use]
1635    pub const fn current_cursor(&self) -> DeliverySeq {
1636        self.current_cursor
1637    }
1638
1639    /// Fixed regression reason.
1640    #[must_use]
1641    pub const fn reason(&self) -> AckRegressionReason {
1642        let _ = self;
1643        AckRegressionReason::BelowCursor
1644    }
1645}
1646
1647/// Permanent terminal Leave result.
1648#[derive(Clone, Debug, PartialEq, Eq)]
1649pub struct LeaveCommitted {
1650    /// Conversation from the request.
1651    conversation_id: ConversationId,
1652    /// Committed Leave token.
1653    leave_attempt_token: LeaveAttemptToken,
1654    /// Retired participant.
1655    participant_id: ParticipantId,
1656    /// Permanent retired generation.
1657    retired_generation: Generation,
1658    /// Active binding ended by this same commit, if any.
1659    ended_binding_epoch: Option<BindingEpoch>,
1660    /// Earlier binding-terminal record, if one exists.
1661    prior_terminal_delivery_seq: Option<DeliverySeq>,
1662    /// Assigned Left delivery sequence.
1663    left_delivery_seq: DeliverySeq,
1664}
1665
1666impl LeaveCommitted {
1667    /// Constructs a terminal Leave outcome from its authoritative durable
1668    /// values.
1669    ///
1670    /// Returns `None` when a supplied active binding carries another
1671    /// generation or when a prior terminal is not strictly before `Left`.
1672    #[must_use]
1673    #[allow(clippy::too_many_arguments)]
1674    pub const fn new(
1675        conversation_id: ConversationId,
1676        leave_attempt_token: LeaveAttemptToken,
1677        participant_id: ParticipantId,
1678        retired_generation: Generation,
1679        ended_binding_epoch: Option<BindingEpoch>,
1680        prior_terminal_delivery_seq: Option<DeliverySeq>,
1681        left_delivery_seq: DeliverySeq,
1682    ) -> Option<Self> {
1683        if let Some(epoch) = ended_binding_epoch
1684            && epoch.capability_generation.get() != retired_generation.get()
1685        {
1686            return None;
1687        }
1688        if let Some(prior) = prior_terminal_delivery_seq
1689            && prior >= left_delivery_seq
1690        {
1691            return None;
1692        }
1693        Some(Self {
1694            conversation_id,
1695            leave_attempt_token,
1696            participant_id,
1697            retired_generation,
1698            ended_binding_epoch,
1699            prior_terminal_delivery_seq,
1700            left_delivery_seq,
1701        })
1702    }
1703
1704    /// Conversation from the request.
1705    #[must_use]
1706    pub const fn conversation_id(&self) -> ConversationId {
1707        self.conversation_id
1708    }
1709
1710    /// Committed Leave token.
1711    #[must_use]
1712    pub const fn leave_attempt_token(&self) -> LeaveAttemptToken {
1713        self.leave_attempt_token
1714    }
1715
1716    /// Retired participant.
1717    #[must_use]
1718    pub const fn participant_id(&self) -> ParticipantId {
1719        self.participant_id
1720    }
1721
1722    /// Presented generation, equal to the permanent retired generation.
1723    #[must_use]
1724    pub const fn presented_generation(&self) -> Generation {
1725        self.retired_generation
1726    }
1727
1728    /// Permanent retired generation.
1729    #[must_use]
1730    pub const fn retired_generation(&self) -> Generation {
1731        self.retired_generation
1732    }
1733
1734    /// Active binding ended by this same commit, if any.
1735    #[must_use]
1736    pub const fn ended_binding_epoch(&self) -> Option<BindingEpoch> {
1737        self.ended_binding_epoch
1738    }
1739
1740    /// Earlier binding-terminal record, if one exists.
1741    #[must_use]
1742    pub const fn prior_terminal_delivery_seq(&self) -> Option<DeliverySeq> {
1743        self.prior_terminal_delivery_seq
1744    }
1745
1746    /// Assigned Left delivery sequence.
1747    #[must_use]
1748    pub const fn left_delivery_seq(&self) -> DeliverySeq {
1749        self.left_delivery_seq
1750    }
1751}
1752
1753/// Marker acknowledgement advanced the cursor.
1754#[derive(Clone, Debug, PartialEq, Eq)]
1755pub struct MarkerAckCommitted {
1756    /// Marker-ack request envelope.
1757    request: MarkerAckEnvelope,
1758}
1759
1760impl MarkerAckCommitted {
1761    /// Constructs a committed marker acknowledgement whose cursor is the
1762    /// requested marker.
1763    #[must_use]
1764    pub const fn new(request: MarkerAckEnvelope) -> Self {
1765        Self { request }
1766    }
1767
1768    /// Marker-ack request envelope.
1769    #[must_use]
1770    pub const fn request(&self) -> &MarkerAckEnvelope {
1771        &self.request
1772    }
1773
1774    /// Resulting marker cursor, equal to `request.marker_delivery_seq`.
1775    #[must_use]
1776    pub const fn current_cursor(&self) -> DeliverySeq {
1777        self.request.marker_delivery_seq
1778    }
1779}
1780
1781/// Ordinary record commit result.
1782#[derive(Clone, Debug, PartialEq, Eq)]
1783pub struct RecordCommitted {
1784    /// Request envelope, without opaque payload.
1785    request: RecordAdmissionEnvelope,
1786    /// Assigned record sequence.
1787    delivery_seq: DeliverySeq,
1788}
1789
1790impl RecordCommitted {
1791    /// Constructs an ordinary commit and derives its verified sender from the
1792    /// authoritative request envelope.
1793    #[must_use]
1794    pub const fn new(request: RecordAdmissionEnvelope, delivery_seq: DeliverySeq) -> Self {
1795        Self {
1796            request,
1797            delivery_seq,
1798        }
1799    }
1800
1801    /// Request envelope, without opaque payload.
1802    #[must_use]
1803    pub const fn request(&self) -> &RecordAdmissionEnvelope {
1804        &self.request
1805    }
1806
1807    /// Verified sender, exactly the request participant.
1808    #[must_use]
1809    pub const fn sender_participant_id(&self) -> ParticipantId {
1810        self.request.participant_id
1811    }
1812
1813    /// Assigned record sequence.
1814    #[must_use]
1815    pub const fn delivery_seq(&self) -> DeliverySeq {
1816        self.delivery_seq
1817    }
1818}
1819
1820/// Ordinary record exceeds configured entry or byte maximum.
1821#[derive(Clone, Debug, PartialEq, Eq)]
1822pub struct RecordTooLarge {
1823    /// Request envelope, without opaque payload.
1824    pub request: RecordAdmissionEnvelope,
1825    /// First failing component.
1826    pub dimension: ResourceDimension,
1827    /// Exact durable record charge.
1828    pub encoded_record_charge: ResourceVector,
1829    /// Configured maximum ordinary record charge.
1830    pub max_ordinary_record_charge: ResourceVector,
1831}
1832
1833/// One observer progress status returned in request order.
1834#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1835pub struct ObserverProgressStatus {
1836    /// Conversation from the request entry.
1837    pub conversation_id: ConversationId,
1838    /// Presented refusal epoch.
1839    pub refused_epoch: ObserverEpoch,
1840    /// Current observer progress.
1841    pub current_observer_progress: DeliverySeq,
1842    /// Whether an equal epoch was atomically armed.
1843    pub armed: bool,
1844    /// Whether the presented epoch was already older/progressed.
1845    pub progressed: bool,
1846}
1847
1848/// Whole-batch observer recovery success.
1849#[derive(Clone, Debug, PartialEq, Eq)]
1850pub struct ObserverRecoveryAccepted {
1851    /// Request-ordered statuses. Wire uses one structural `u64` count only.
1852    pub statuses: Vec<ObserverProgressStatus>,
1853}
1854
1855/// Whole-batch invalid observer epoch.
1856#[derive(Clone, Debug, PartialEq, Eq)]
1857pub enum InvalidObserverEpoch {
1858    /// Conversation does not exist; current progress option is encoded `None`.
1859    ConversationUnknown {
1860        /// Unknown conversation.
1861        conversation_id: ConversationId,
1862        /// Presented epoch.
1863        presented_epoch: ObserverEpoch,
1864    },
1865    /// Presented epoch is ahead; current progress option is encoded `Some`.
1866    EpochAhead {
1867        /// Known conversation.
1868        conversation_id: ConversationId,
1869        /// Presented newer epoch.
1870        presented_epoch: ObserverEpoch,
1871        /// Current observer progress.
1872        current_observer_progress: DeliverySeq,
1873    },
1874}
1875
1876impl InvalidObserverEpoch {
1877    /// Returns the exact scalar reason tag implied by the selected body.
1878    #[must_use]
1879    pub const fn reason(&self) -> InvalidObserverEpochReason {
1880        match self {
1881            Self::ConversationUnknown { .. } => InvalidObserverEpochReason::ConversationUnknown,
1882            Self::EpochAhead { .. } => InvalidObserverEpochReason::EpochAhead,
1883        }
1884    }
1885}
1886
1887/// Whole-batch invalid observer recovery list.
1888#[derive(Clone, Debug, PartialEq, Eq)]
1889pub enum InvalidObserverEpochList {
1890    /// Request exceeds its signed entry limit.
1891    TooManyEntries {
1892        /// Presented list length.
1893        presented_entries: u64,
1894        /// Signed maximum entries.
1895        max_entries: u64,
1896    },
1897    /// Request repeats a conversation.
1898    DuplicateConversation {
1899        /// Repeated conversation.
1900        conversation_id: ConversationId,
1901        /// First request index.
1902        first_index: u64,
1903        /// Repeated request index.
1904        duplicate_index: u64,
1905    },
1906}
1907
1908impl InvalidObserverEpochList {
1909    /// Returns the exact scalar reason tag implied by the selected body.
1910    #[must_use]
1911    pub const fn reason(&self) -> InvalidObserverEpochListReason {
1912        match self {
1913            Self::TooManyEntries { .. } => InvalidObserverEpochListReason::TooManyEntries,
1914            Self::DuplicateConversation { .. } => {
1915                InvalidObserverEpochListReason::DuplicateConversation
1916            }
1917        }
1918    }
1919}
1920
1921/// Outcome of a read-only record settlement.
1922#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1923pub enum SettlementOutcome {
1924    /// The original admission's durable sequence.
1925    Committed {
1926        /// Original durable delivery sequence.
1927        delivery_seq: DeliverySeq,
1928    },
1929    /// The token was explicitly withdrawn.
1930    Withdrawn,
1931    /// Its retained authority has expired.
1932    Expired,
1933    /// Absence is proved by the current durable owner.
1934    NotCommitted,
1935    /// The current owner cannot prove the outcome.
1936    Unknown,
1937}
1938
1939/// Exact presenter and token echoed by a settlement answer.
1940#[derive(Clone, Debug, PartialEq, Eq)]
1941pub struct SettlementAnswer {
1942    /// Queried conversation.
1943    pub conversation_id: ConversationId,
1944    /// Queried presenter.
1945    pub participant_id: ParticipantId,
1946    /// Original staged token.
1947    pub record_admission_attempt_token: RecordAdmissionAttemptToken,
1948    /// Proven outcome.
1949    pub outcome: SettlementOutcome,
1950}
1951
1952/// Exhaustive server-to-client semantic participant value.
1953#[derive(Clone, Debug, PartialEq, Eq)]
1954pub enum ServerValue {
1955    /// `0x0100`.
1956    ParticipantTransportRejected(ParticipantTransportRejected),
1957    /// `0x0101`.
1958    AttemptTokenBodyConflict(AttemptTokenBodyConflict),
1959    /// `0x0102` or `0x0124`, selected by the inner exact schema.
1960    ConnectionConversationCapacityExceeded(ConnectionConversationCapacityExceeded),
1961    /// `0x0103`.
1962    ConnectionConversationBindingOccupied(ConnectionConversationBindingOccupied),
1963    /// `0x0104`.
1964    ConversationOrderExhausted(Box<ConversationOrderExhausted>),
1965    /// `0x0105`.
1966    ParticipantUnknown(ParticipantUnknown),
1967    /// `0x0106`.
1968    NoBinding(NoBinding),
1969    /// `0x0107`.
1970    StaleAuthority(StaleAuthority),
1971    /// `0x0108`.
1972    Retired(Retired),
1973    /// `0x0109`.
1974    MarkerClosureCapacityExceeded(Box<MarkerClosureCapacityExceeded>),
1975    /// `0x010A`.
1976    EnrollBound(EnrollBound),
1977    /// `0x010B`.
1978    EnrollmentKnown(EnrollmentKnown),
1979    /// `0x010C`.
1980    ReceiptExpired(ReceiptExpired),
1981    /// `0x010D`.
1982    ReceiptCapacityExceeded(ReceiptCapacityExceeded),
1983    /// `0x010E`.
1984    IdentityCapacityExceeded(IdentityCapacityExceeded),
1985    /// `0x010F`.
1986    ObserverBackpressure(ObserverBackpressure),
1987    /// `0x0110`.
1988    ConversationSequenceExhausted(Box<ConversationSequenceExhausted>),
1989    /// `0x0111`.
1990    AttachBound(AttachBound),
1991    /// `0x0112`.
1992    StaleOrUnknownReceipt(StaleOrUnknownReceipt),
1993    /// `0x0113`.
1994    MarkerNotDelivered(MarkerNotDelivered),
1995    /// `0x0114`.
1996    MarkerMismatch(MarkerMismatch),
1997    /// `0x0115`.
1998    Bound(ReceiptReplay),
1999    /// `0x0116`.
2000    UnboundReceipt(ReceiptReplay),
2001    /// `0x0117`.
2002    DetachCommitted(DetachCommitted),
2003    /// `0x0118`.
2004    DetachInProgress(DetachInProgress),
2005    /// `0x0119`.
2006    AckCommitted(AckCommitted),
2007    /// `0x011A`.
2008    AckNoOp(AckNoOp),
2009    /// `0x011B`.
2010    AckGap(AckGap),
2011    /// `0x011C`.
2012    AckRegression(AckRegression),
2013    /// `0x011D`.
2014    LeaveCommitted(LeaveCommitted),
2015    /// `0x011E`.
2016    MarkerAckCommitted(MarkerAckCommitted),
2017    /// `0x011F`.
2018    RecordCommitted(RecordCommitted),
2019    /// `0x0120`.
2020    RecordTooLarge(RecordTooLarge),
2021    /// `0x0121`.
2022    ObserverRecoveryAccepted(ObserverRecoveryAccepted),
2023    /// `0x0122`.
2024    InvalidObserverEpoch(InvalidObserverEpoch),
2025    /// `0x0123`.
2026    InvalidObserverEpochList(InvalidObserverEpochList),
2027    /// `0x0125`.
2028    MarkerSettlementBackpressure(MarkerSettlementBackpressure),
2029    /// `0x0126`.
2030    EnrollmentSettlementBackpressure(EnrollmentSettlementBackpressure),
2031    /// `0x0127`.
2032    RecordAdmissionProtocolFault(RecordAdmissionProtocolFault),
2033    /// `0x0128`.
2034    WriteOutcomeUnknown(WriteOutcomeUnknown),
2035    /// Read-only settlement outcome.
2036    SettlementAnswer(SettlementAnswer),
2037}
2038
2039impl ServerValue {
2040    /// Returns the exact contiguous server value discriminant.
2041    #[must_use]
2042    pub const fn discriminant(&self) -> ServerDiscriminant {
2043        match self {
2044            Self::ParticipantTransportRejected(_) => {
2045                ServerDiscriminant::ParticipantTransportRejected
2046            }
2047            Self::AttemptTokenBodyConflict(_) => ServerDiscriminant::AttemptTokenBodyConflict,
2048            Self::ConnectionConversationCapacityExceeded(value) => match value {
2049                ConnectionConversationCapacityExceeded::SemanticRequest { .. } => {
2050                    ServerDiscriminant::ConnectionConversationCapacityExceeded
2051                }
2052                ConnectionConversationCapacityExceeded::ObserverRecovery { .. } => {
2053                    ServerDiscriminant::ObserverRecoveryConnectionCapacityExceeded
2054                }
2055            },
2056            Self::ConnectionConversationBindingOccupied(_) => {
2057                ServerDiscriminant::ConnectionConversationBindingOccupied
2058            }
2059            Self::ConversationOrderExhausted(_) => ServerDiscriminant::ConversationOrderExhausted,
2060            Self::ParticipantUnknown(_) => ServerDiscriminant::ParticipantUnknown,
2061            Self::NoBinding(_) => ServerDiscriminant::NoBinding,
2062            Self::StaleAuthority(_) => ServerDiscriminant::StaleAuthority,
2063            Self::Retired(_) => ServerDiscriminant::Retired,
2064            Self::MarkerClosureCapacityExceeded(_) => {
2065                ServerDiscriminant::MarkerClosureCapacityExceeded
2066            }
2067            Self::EnrollBound(_) => ServerDiscriminant::EnrollBound,
2068            Self::EnrollmentKnown(_) => ServerDiscriminant::EnrollmentKnown,
2069            Self::ReceiptExpired(_) => ServerDiscriminant::ReceiptExpired,
2070            Self::ReceiptCapacityExceeded(_) => ServerDiscriminant::ReceiptCapacityExceeded,
2071            Self::IdentityCapacityExceeded(_) => ServerDiscriminant::IdentityCapacityExceeded,
2072            Self::ObserverBackpressure(_) => ServerDiscriminant::ObserverBackpressure,
2073            Self::ConversationSequenceExhausted(_) => {
2074                ServerDiscriminant::ConversationSequenceExhausted
2075            }
2076            Self::AttachBound(_) => ServerDiscriminant::AttachBound,
2077            Self::StaleOrUnknownReceipt(_) => ServerDiscriminant::StaleOrUnknownReceipt,
2078            Self::MarkerNotDelivered(_) => ServerDiscriminant::MarkerNotDelivered,
2079            Self::MarkerMismatch(_) => ServerDiscriminant::MarkerMismatch,
2080            Self::Bound(_) => ServerDiscriminant::Bound,
2081            Self::UnboundReceipt(_) => ServerDiscriminant::UnboundReceipt,
2082            Self::DetachCommitted(_) => ServerDiscriminant::DetachCommitted,
2083            Self::DetachInProgress(_) => ServerDiscriminant::DetachInProgress,
2084            Self::AckCommitted(_) => ServerDiscriminant::AckCommitted,
2085            Self::AckNoOp(_) => ServerDiscriminant::AckNoOp,
2086            Self::AckGap(_) => ServerDiscriminant::AckGap,
2087            Self::AckRegression(_) => ServerDiscriminant::AckRegression,
2088            Self::LeaveCommitted(_) => ServerDiscriminant::LeaveCommitted,
2089            Self::MarkerAckCommitted(_) => ServerDiscriminant::MarkerAckCommitted,
2090            Self::RecordCommitted(_) => ServerDiscriminant::RecordCommitted,
2091            Self::RecordTooLarge(_) => ServerDiscriminant::RecordTooLarge,
2092            Self::ObserverRecoveryAccepted(_) => ServerDiscriminant::ObserverRecoveryAccepted,
2093            Self::InvalidObserverEpoch(_) => ServerDiscriminant::InvalidObserverEpoch,
2094            Self::InvalidObserverEpochList(_) => ServerDiscriminant::InvalidObserverEpochList,
2095            Self::MarkerSettlementBackpressure(_) => {
2096                ServerDiscriminant::MarkerSettlementBackpressure
2097            }
2098            Self::EnrollmentSettlementBackpressure(_) => {
2099                ServerDiscriminant::EnrollmentSettlementBackpressure
2100            }
2101            Self::RecordAdmissionProtocolFault(_) => {
2102                ServerDiscriminant::RecordAdmissionProtocolFault
2103            }
2104            Self::WriteOutcomeUnknown(_) => ServerDiscriminant::WriteOutcomeUnknown,
2105            Self::SettlementAnswer(_) => ServerDiscriminant::SettlementAnswer,
2106        }
2107    }
2108
2109    /// Returns the structural originating-request selector when the value has one.
2110    #[must_use]
2111    #[allow(clippy::too_many_lines)]
2112    pub const fn originating_request(&self) -> Option<ClientDiscriminant> {
2113        match self {
2114            Self::ParticipantTransportRejected(_)
2115            | Self::ObserverRecoveryAccepted(_)
2116            | Self::InvalidObserverEpoch(_)
2117            | Self::InvalidObserverEpochList(_) => None,
2118            Self::AttemptTokenBodyConflict(value) => Some(match value {
2119                AttemptTokenBodyConflict::CredentialAttach { .. } => {
2120                    ClientDiscriminant::CredentialAttachRequest
2121                }
2122                AttemptTokenBodyConflict::Leave { .. } => ClientDiscriminant::LeaveRequest,
2123                AttemptTokenBodyConflict::RecordAdmission { .. } => {
2124                    ClientDiscriminant::RecordAdmission
2125                }
2126            }),
2127            Self::ConnectionConversationCapacityExceeded(value) => match value {
2128                ConnectionConversationCapacityExceeded::SemanticRequest { request, .. } => {
2129                    Some(request.originating_request())
2130                }
2131                ConnectionConversationCapacityExceeded::ObserverRecovery { .. } => None,
2132            },
2133            Self::ConnectionConversationBindingOccupied(value) => Some(match value {
2134                ConnectionConversationBindingOccupied::Enrollment { .. } => {
2135                    ClientDiscriminant::EnrollmentRequest
2136                }
2137                ConnectionConversationBindingOccupied::CredentialAttach { .. } => {
2138                    ClientDiscriminant::CredentialAttachRequest
2139                }
2140            }),
2141            Self::ConversationOrderExhausted(value) => Some(match value.request() {
2142                OrderAllocatingEnvelope::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
2143                OrderAllocatingEnvelope::CredentialAttach(_) => {
2144                    ClientDiscriminant::CredentialAttachRequest
2145                }
2146                OrderAllocatingEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2147            }),
2148            Self::ParticipantUnknown(value) => Some(participant_reference_origin(&value.request)),
2149            Self::NoBinding(value) => Some(binding_required_origin(&value.request)),
2150            Self::StaleAuthority(value) => Some(stale_authority_origin(value)),
2151            Self::Retired(value) => Some(match value {
2152                Retired::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2153                Retired::Participant { request, .. } => participant_reference_origin(request),
2154            }),
2155            Self::MarkerClosureCapacityExceeded(value) => Some(match &value.request {
2156                ClosureCheckedEnvelope::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
2157                ClosureCheckedEnvelope::CredentialAttach(_) => {
2158                    ClientDiscriminant::CredentialAttachRequest
2159                }
2160                ClosureCheckedEnvelope::Leave(_) => ClientDiscriminant::LeaveRequest,
2161                ClosureCheckedEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2162            }),
2163            Self::EnrollBound(_)
2164            | Self::EnrollmentKnown(_)
2165            | Self::IdentityCapacityExceeded(_)
2166            | Self::EnrollmentSettlementBackpressure(_) => {
2167                Some(ClientDiscriminant::EnrollmentRequest)
2168            }
2169            Self::ReceiptExpired(value) => Some(match value {
2170                ReceiptExpired::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2171                ReceiptExpired::CredentialAttach { .. } => {
2172                    ClientDiscriminant::CredentialAttachRequest
2173                }
2174            }),
2175            Self::ReceiptCapacityExceeded(value) => Some(match value {
2176                ReceiptCapacityExceeded::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2177                ReceiptCapacityExceeded::CredentialAttach { .. } => {
2178                    ClientDiscriminant::CredentialAttachRequest
2179                }
2180            }),
2181            Self::ObserverBackpressure(value) => Some(match value {
2182                ObserverBackpressure::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2183                ObserverBackpressure::CredentialAttach { .. } => {
2184                    ClientDiscriminant::CredentialAttachRequest
2185                }
2186                ObserverBackpressure::Detach { .. } => ClientDiscriminant::DetachRequest,
2187                ObserverBackpressure::Leave { .. } => ClientDiscriminant::LeaveRequest,
2188                ObserverBackpressure::RecordAdmission { .. } => ClientDiscriminant::RecordAdmission,
2189            }),
2190            Self::ConversationSequenceExhausted(value) => Some(match &value.request {
2191                SequenceAllocatingEnvelope::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
2192                SequenceAllocatingEnvelope::CredentialAttach(_) => {
2193                    ClientDiscriminant::CredentialAttachRequest
2194                }
2195                SequenceAllocatingEnvelope::RecordAdmission(_) => {
2196                    ClientDiscriminant::RecordAdmission
2197                }
2198            }),
2199            Self::AttachBound(_) | Self::StaleOrUnknownReceipt(_) => {
2200                Some(ClientDiscriminant::CredentialAttachRequest)
2201            }
2202            Self::MarkerNotDelivered(value) => Some(marker_proof_origin(&value.request)),
2203            Self::MarkerMismatch(value) => Some(marker_proof_origin(&value.request)),
2204            Self::Bound(value) | Self::UnboundReceipt(value) => Some(match value {
2205                ReceiptReplay::Enrollment(_) => ClientDiscriminant::EnrollmentRequest,
2206                ReceiptReplay::CredentialAttach(_) => ClientDiscriminant::CredentialAttachRequest,
2207            }),
2208            Self::DetachCommitted(_) | Self::DetachInProgress(_) => {
2209                Some(ClientDiscriminant::DetachRequest)
2210            }
2211            Self::AckCommitted(_) | Self::AckGap(_) | Self::AckRegression(_) => {
2212                Some(ClientDiscriminant::ParticipantAck)
2213            }
2214            Self::AckNoOp(value) => Some(match value {
2215                AckNoOp::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2216                AckNoOp::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2217            }),
2218            Self::LeaveCommitted(_) => Some(ClientDiscriminant::LeaveRequest),
2219            Self::MarkerAckCommitted(_) => Some(ClientDiscriminant::MarkerAck),
2220            Self::RecordCommitted(_)
2221            | Self::RecordTooLarge(_)
2222            | Self::RecordAdmissionProtocolFault(_) => Some(ClientDiscriminant::RecordAdmission),
2223            Self::MarkerSettlementBackpressure(value) => Some(match value {
2224                MarkerSettlementBackpressure::CredentialAttach { .. } => {
2225                    ClientDiscriminant::CredentialAttachRequest
2226                }
2227                MarkerSettlementBackpressure::Detach { .. } => ClientDiscriminant::DetachRequest,
2228            }),
2229            Self::SettlementAnswer(_) => Some(ClientDiscriminant::SettlementQuery),
2230            Self::WriteOutcomeUnknown(value) => Some(match value {
2231                WriteOutcomeUnknown::Enrollment { .. } => ClientDiscriminant::EnrollmentRequest,
2232                WriteOutcomeUnknown::CredentialAttach { .. } => {
2233                    ClientDiscriminant::CredentialAttachRequest
2234                }
2235                WriteOutcomeUnknown::Detach { .. } => ClientDiscriminant::DetachRequest,
2236                WriteOutcomeUnknown::Leave { .. } => ClientDiscriminant::LeaveRequest,
2237                WriteOutcomeUnknown::RecordAdmission { .. } => ClientDiscriminant::RecordAdmission,
2238            }),
2239        }
2240    }
2241}
2242
2243const fn participant_reference_origin(
2244    request: &ParticipantReferenceEnvelope,
2245) -> ClientDiscriminant {
2246    match request {
2247        ParticipantReferenceEnvelope::CredentialAttach(_) => {
2248            ClientDiscriminant::CredentialAttachRequest
2249        }
2250        ParticipantReferenceEnvelope::Detach(_) => ClientDiscriminant::DetachRequest,
2251        ParticipantReferenceEnvelope::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2252        ParticipantReferenceEnvelope::Leave(_) => ClientDiscriminant::LeaveRequest,
2253        ParticipantReferenceEnvelope::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2254        ParticipantReferenceEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2255    }
2256}
2257
2258const fn binding_required_origin(request: &BindingRequiredEnvelope) -> ClientDiscriminant {
2259    match request {
2260        BindingRequiredEnvelope::Detach(_) => ClientDiscriminant::DetachRequest,
2261        BindingRequiredEnvelope::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2262        BindingRequiredEnvelope::Leave(_) => ClientDiscriminant::LeaveRequest,
2263        BindingRequiredEnvelope::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2264        BindingRequiredEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2265    }
2266}
2267
2268const fn stale_authority_origin(value: &StaleAuthority) -> ClientDiscriminant {
2269    match value {
2270        StaleAuthority::Live { request, .. } => match request {
2271            CommonStaleAuthorityEnvelope::CredentialAttach(_) => {
2272                ClientDiscriminant::CredentialAttachRequest
2273            }
2274            CommonStaleAuthorityEnvelope::ParticipantAck(_) => ClientDiscriminant::ParticipantAck,
2275            CommonStaleAuthorityEnvelope::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2276            CommonStaleAuthorityEnvelope::RecordAdmission(_) => ClientDiscriminant::RecordAdmission,
2277        },
2278        StaleAuthority::Detach(_) => ClientDiscriminant::DetachRequest,
2279        StaleAuthority::Leave(_) => ClientDiscriminant::LeaveRequest,
2280    }
2281}
2282
2283const fn marker_proof_origin(request: &MarkerProofRequest) -> ClientDiscriminant {
2284    match request {
2285        MarkerProofRequest::CredentialAttach(_) => ClientDiscriminant::CredentialAttachRequest,
2286        MarkerProofRequest::MarkerAck(_) => ClientDiscriminant::MarkerAck,
2287    }
2288}
2289
2290/// Exact capability string serialized by transport capability refusal.
2291pub const PARTICIPANT_CAPABILITY: &str = "participant-v1";
2292
2293/// Returns the exact attempt operation implied by a body-conflict variant.
2294#[must_use]
2295pub const fn attempt_operation(value: &AttemptTokenBodyConflict) -> super::AttemptOperation {
2296    match value {
2297        AttemptTokenBodyConflict::CredentialAttach { .. } => {
2298            super::AttemptOperation::CredentialAttachRequest
2299        }
2300        AttemptTokenBodyConflict::Leave { .. } => super::AttemptOperation::LeaveRequest,
2301        AttemptTokenBodyConflict::RecordAdmission { .. } => {
2302            super::AttemptOperation::RecordAdmission
2303        }
2304    }
2305}
2306
2307/// Owns a capability string after wire decoding while retaining domain validation.
2308#[must_use]
2309pub fn capability_string() -> String {
2310    String::from(PARTICIPANT_CAPABILITY)
2311}