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}