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