Skip to main content

liminal_protocol/wire/
response.rs

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