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