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, RecordCommitted, RecordTooLarge, ResponseEnvelope, Retired,
12    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    /// Borrows the bound wire value for encoding or inspection.
185    #[must_use]
186    pub const fn server_value(&self) -> &ServerValue {
187        &self.value
188    }
189
190    /// Returns the bound value's exact wire discriminant.
191    #[must_use]
192    pub const fn discriminant(&self) -> ServerDiscriminant {
193        self.value.discriminant()
194    }
195
196    /// Moves the bound wire value out for transmission.
197    #[must_use]
198    pub fn into_server_value(self) -> ServerValue {
199        self.value
200    }
201}
202
203/// Server response bound to one observer-recovery handshake batch.
204///
205/// The register admits exactly four outcomes for the one-shot recovery batch
206/// (rows 5642, 5688, 5689); the contract's routing rule (lines 5780-5782)
207/// marks all four as already request-specific, so they carry no
208/// `originating_request` echo. Every other pairing is a compile error by
209/// construction.
210#[derive(Clone, Debug, PartialEq, Eq)]
211pub struct ObserverRecoveryResponse {
212    value: ServerValue,
213}
214
215impl ObserverRecoveryResponse {
216    /// Batch preflight found an untracked conversation that would exceed the
217    /// signed connection-conversation limit (register row 5642, wire
218    /// `0x0124`).
219    #[must_use]
220    pub const fn connection_capacity_exceeded(conversation_id: ConversationId, limit: u64) -> Self {
221        Self {
222            value: ServerValue::ConnectionConversationCapacityExceeded(
223                ConnectionConversationCapacityExceeded::ObserverRecovery {
224                    conversation_id,
225                    limit,
226                },
227            ),
228        }
229    }
230
231    /// Whole-batch success with request-ordered statuses (register row 5688).
232    #[must_use]
233    pub const fn accepted(value: ObserverRecoveryAccepted) -> Self {
234        Self {
235            value: ServerValue::ObserverRecoveryAccepted(value),
236        }
237    }
238
239    /// Whole-batch unknown-conversation or ahead-epoch refusal (register row
240    /// 5689).
241    #[must_use]
242    pub const fn invalid_observer_epoch(value: InvalidObserverEpoch) -> Self {
243        Self {
244            value: ServerValue::InvalidObserverEpoch(value),
245        }
246    }
247
248    /// Whole-batch list-shape refusal (register row 5689).
249    #[must_use]
250    pub const fn invalid_observer_epoch_list(value: InvalidObserverEpochList) -> Self {
251        Self {
252            value: ServerValue::InvalidObserverEpochList(value),
253        }
254    }
255
256    /// Borrows the bound wire value for encoding or inspection.
257    #[must_use]
258    pub const fn server_value(&self) -> &ServerValue {
259        &self.value
260    }
261
262    /// Returns the bound value's exact wire discriminant.
263    #[must_use]
264    pub const fn discriminant(&self) -> ServerDiscriminant {
265        self.value.discriminant()
266    }
267
268    /// Moves the bound wire value out for transmission.
269    #[must_use]
270    pub fn into_server_value(self) -> ServerValue {
271        self.value
272    }
273}