Skip to main content

liminal_protocol/wire/authority/
records.rs

1//! Response authorities bound to `ClientRequest::RecordAdmission` (`0x0007`)
2//! and `ClientRequest::ObserverRecovery` (`0x0008`).
3
4use alloc::boxed::Box;
5
6use super::super::{
7    AttemptTokenBodyConflict, ConnectionConversationCapacityExceeded, ConversationId,
8    ConversationOrderExhausted, ConversationSequenceExhausted, Generation, InvalidObserverEpoch,
9    InvalidObserverEpochList, MarkerClosureCapacityExceeded, NoBinding, ObserverBackpressure,
10    ObserverRecoveryAccepted, ParticipantId, ParticipantUnknown, RecordAdmissionAttemptToken,
11    RecordAdmissionEnvelope, RecordAdmissionProtocolFault, RecordCommitted, RecordTooLarge,
12    ResponseEnvelope, Retired, ServerDiscriminant, ServerValue, StaleAuthority,
13};
14
15/// Server response bound to one ordinary record admission.
16///
17/// Constructors exist only for the outcomes the frozen R-D1 register admits
18/// for ordinary admission; every other pairing is a compile error by
19/// construction.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct RecordAdmissionResponse {
22    value: ServerValue,
23}
24
25impl RecordAdmissionResponse {
26    /// Exact committed attempt token re-presented by its OWN verified
27    /// participant under different canonical payload bytes (register row 5639,
28    /// whose admitted-request set gains `RecordAdmission` under contract
29    /// ยง0.15 amendment A4).
30    ///
31    /// Ordinary admission selects no [`AttemptConflict`](super::super::AttemptConflict)
32    /// selector, in the same way Leave selects no marker one: the committed
33    /// identity is the (token, canonical-payload fingerprint, verified
34    /// participant) triple, so the one conflicting axis is the canonical body
35    /// this row is named for.
36    ///
37    /// โ›” Only the SAME-participant arm may reach this constructor. A token
38    /// hit belonging to a DIFFERENT participant is a dedup miss that commits
39    /// silently: any token-correlated answer across participants is a probe
40    /// channel A4 outlaws permanently, and the server-side range that finds
41    /// such a hit stays warn-and-fall-through.
42    #[must_use]
43    pub const fn attempt_token_body_conflict(
44        token: RecordAdmissionAttemptToken,
45        conversation_id: ConversationId,
46        presented_participant_id: ParticipantId,
47        presented_generation: Generation,
48    ) -> Self {
49        Self {
50            value: ServerValue::AttemptTokenBodyConflict(
51                AttemptTokenBodyConflict::RecordAdmission {
52                    token,
53                    conversation_id,
54                    presented_participant_id,
55                    presented_generation,
56                },
57            ),
58        }
59    }
60
61    /// First decoded semantic operation for an untracked conversation
62    /// exceeded the connection-conversation limit (register row 5641).
63    #[must_use]
64    pub const fn connection_conversation_capacity_exceeded(
65        request: RecordAdmissionEnvelope,
66        limit: u64,
67    ) -> Self {
68        Self {
69            value: ServerValue::ConnectionConversationCapacityExceeded(
70                ConnectionConversationCapacityExceeded::SemanticRequest {
71                    request: ResponseEnvelope::RecordAdmission(request),
72                    limit,
73                },
74            ),
75        }
76    }
77
78    /// Ordinary admission required an unreserved `transaction_order` major
79    /// and the conversation order is exhausted (register row 5644).
80    ///
81    /// The payload is minted only by the shared order allocator invoked with
82    /// this request's own envelope.
83    pub(crate) const fn from_conversation_order_exhausted(
84        value: Box<ConversationOrderExhausted>,
85    ) -> Self {
86        Self {
87            value: ServerValue::ConversationOrderExhausted(value),
88        }
89    }
90
91    /// Presented participant is unknown (register row 5645).
92    ///
93    /// The payload is minted only by `lookup_binding_required` for this
94    /// exact request.
95    pub(crate) const fn from_participant_unknown(value: ParticipantUnknown) -> Self {
96        Self {
97            value: ServerValue::ParticipantUnknown(value),
98        }
99    }
100
101    /// Exact-binding lookup missed (register row 5646).
102    ///
103    /// The payload is minted only by `lookup_binding_required` for this
104    /// exact request.
105    pub(crate) const fn from_no_binding(value: NoBinding) -> Self {
106        Self {
107            value: ServerValue::NoBinding(value),
108        }
109    }
110
111    /// Live generation authority is stale (register row 5647).
112    ///
113    /// The payload is minted only by `lookup_binding_required` for this
114    /// exact request.
115    pub(crate) const fn from_stale_authority(value: StaleAuthority) -> Self {
116        Self {
117            value: ServerValue::StaleAuthority(value),
118        }
119    }
120
121    /// Presented id has a tombstone (register row 5648).
122    ///
123    /// The payload is minted only by `lookup_binding_required` for this
124    /// exact request.
125    pub(crate) const fn from_retired(value: Retired) -> Self {
126        Self {
127            value: ServerValue::Retired(value),
128        }
129    }
130
131    /// Closure-checked ordinary admission exceeded marker-closure capacity
132    /// (register rows 5649, 5686).
133    ///
134    /// The payload is minted only by the shared remaining-closure selector
135    /// invoked with this request's own envelope.
136    pub(crate) const fn from_marker_closure_capacity_exceeded(
137        value: Box<MarkerClosureCapacityExceeded>,
138    ) -> Self {
139        Self {
140            value: ServerValue::MarkerClosureCapacityExceeded(value),
141        }
142    }
143
144    /// The ordinary record committed (register row 5685).
145    #[must_use]
146    pub const fn record_committed(value: RecordCommitted) -> Self {
147        Self {
148            value: ServerValue::RecordCommitted(value),
149        }
150    }
151
152    /// The record exceeds the configured entry or byte maximum (register row
153    /// 5686).
154    #[must_use]
155    pub const fn record_too_large(value: RecordTooLarge) -> Self {
156        Self {
157            value: ServerValue::RecordTooLarge(value),
158        }
159    }
160
161    /// Canonical resulting sequence-reserve check failed (register row 5686).
162    ///
163    /// The payload is minted only by the shared sequence allocator invoked
164    /// with this request's own envelope.
165    pub(crate) const fn from_conversation_sequence_exhausted(
166        value: Box<ConversationSequenceExhausted>,
167    ) -> Self {
168        Self {
169            value: ServerValue::ConversationSequenceExhausted(value),
170        }
171    }
172
173    /// Hard-observer retention refused the ordinary append (register row
174    /// 5687).
175    ///
176    /// The payload is minted only by the shared observer-floor selector
177    /// invoked with this request's own envelope.
178    pub(crate) const fn from_observer_backpressure(value: ObserverBackpressure) -> Self {
179        Self {
180            value: ServerValue::ObserverBackpressure(value),
181        }
182    }
183
184    /// The spine named an internal protocol fault and terminally refuses this
185    /// admission (register row: ordinary-admission terminal protocol fault,
186    /// amended 2026-08-28).
187    ///
188    /// โ›” CRATE-VISIBLE ON PURPOSE. Every other terminal outcome above is
189    /// minted from a payload some shared selector produced for this exact
190    /// request; this one is minted ONLY by
191    /// [`RecordAdmissionFailure::into_terminal_refusal`](crate::lifecycle::RecordAdmissionFailure::into_terminal_refusal),
192    /// which requires the failure aggregate the total selector actually
193    /// returned. A server cannot choose the class, cannot mint the row without
194    /// a real fault in hand, and gets the unchanged owner back in the same
195    /// move โ€” so "answered the client" and "kept the conversation usable"
196    /// cannot come apart.
197    pub(crate) const fn from_protocol_fault(value: RecordAdmissionProtocolFault) -> Self {
198        Self {
199            value: ServerValue::RecordAdmissionProtocolFault(value),
200        }
201    }
202
203    /// Borrows the bound wire value for encoding or inspection.
204    #[must_use]
205    pub const fn server_value(&self) -> &ServerValue {
206        &self.value
207    }
208
209    /// Returns the bound value's exact wire discriminant.
210    #[must_use]
211    pub const fn discriminant(&self) -> ServerDiscriminant {
212        self.value.discriminant()
213    }
214
215    /// Moves the bound wire value out for transmission.
216    #[must_use]
217    pub fn into_server_value(self) -> ServerValue {
218        self.value
219    }
220}
221
222/// Server response bound to one observer-recovery handshake batch.
223///
224/// The register admits exactly four outcomes for the one-shot recovery batch
225/// (rows 5642, 5688, 5689); the contract's routing rule (lines 5780-5782)
226/// marks all four as already request-specific, so they carry no
227/// `originating_request` echo. Every other pairing is a compile error by
228/// construction.
229#[derive(Clone, Debug, PartialEq, Eq)]
230pub struct ObserverRecoveryResponse {
231    value: ServerValue,
232}
233
234impl ObserverRecoveryResponse {
235    /// Batch preflight found an untracked conversation that would exceed the
236    /// signed connection-conversation limit (register row 5642, wire
237    /// `0x0124`).
238    #[must_use]
239    pub const fn connection_capacity_exceeded(conversation_id: ConversationId, limit: u64) -> Self {
240        Self {
241            value: ServerValue::ConnectionConversationCapacityExceeded(
242                ConnectionConversationCapacityExceeded::ObserverRecovery {
243                    conversation_id,
244                    limit,
245                },
246            ),
247        }
248    }
249
250    /// Whole-batch success with request-ordered statuses (register row 5688).
251    #[must_use]
252    pub const fn accepted(value: ObserverRecoveryAccepted) -> Self {
253        Self {
254            value: ServerValue::ObserverRecoveryAccepted(value),
255        }
256    }
257
258    /// Whole-batch unknown-conversation or ahead-epoch refusal (register row
259    /// 5689).
260    #[must_use]
261    pub const fn invalid_observer_epoch(value: InvalidObserverEpoch) -> Self {
262        Self {
263            value: ServerValue::InvalidObserverEpoch(value),
264        }
265    }
266
267    /// Whole-batch list-shape refusal (register row 5689).
268    #[must_use]
269    pub const fn invalid_observer_epoch_list(value: InvalidObserverEpochList) -> Self {
270        Self {
271            value: ServerValue::InvalidObserverEpochList(value),
272        }
273    }
274
275    /// Borrows the bound wire value for encoding or inspection.
276    #[must_use]
277    pub const fn server_value(&self) -> &ServerValue {
278        &self.value
279    }
280
281    /// Returns the bound value's exact wire discriminant.
282    #[must_use]
283    pub const fn discriminant(&self) -> ServerDiscriminant {
284        self.value.discriminant()
285    }
286
287    /// Moves the bound wire value out for transmission.
288    #[must_use]
289    pub fn into_server_value(self) -> ServerValue {
290        self.value
291    }
292}