liminal_protocol/outcome/local.rs
1use crate::wire::{ConversationId, Generation, ParticipantId};
2
3/// Complete SDK-local participant request exceeded `min(R, WF)`.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub struct SdkParticipantRequestTooLarge {
6 /// Conversation named by the unsent request.
7 pub conversation_id: ConversationId,
8 /// Complete encoded participant request bytes.
9 pub encoded_bytes: u64,
10 /// Exact signed effective request limit `R_send`.
11 pub limit: u64,
12}
13
14/// Closed five-way SDK parking-capacity outcome.
15///
16/// Encoding the scope/dimension pair in the enum makes the forbidden
17/// `PerConversation/Conversations` combination unconstructible.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum SdkObserverParkCapacityExceeded {
20 /// Per-conversation row count exceeded `N`.
21 PerConversationRows {
22 /// Conversation receiving the row.
23 conversation_id: ConversationId,
24 /// Signed per-conversation row limit.
25 limit: u64,
26 /// Current rows in the conversation.
27 occupied: u64,
28 /// Full requested row count.
29 requested: u64,
30 },
31 /// Per-conversation charged bytes exceeded `C`.
32 PerConversationBytes {
33 /// Conversation receiving the row.
34 conversation_id: ConversationId,
35 /// Signed per-conversation byte limit.
36 limit: u64,
37 /// Current full-row bytes in the conversation.
38 occupied: u64,
39 /// Exact full-row bytes requested.
40 requested: u64,
41 },
42 /// SDK-wide parked-conversation count exceeded `P`.
43 SdkWideConversations {
44 /// Conversation requiring its first parked slot.
45 conversation_id: ConversationId,
46 /// Signed SDK conversation limit.
47 limit: u64,
48 /// Current parked-conversation count.
49 occupied: u64,
50 /// Requested conversations; v1 requests one.
51 requested: u64,
52 },
53 /// SDK-wide parked-row count exceeded `G`.
54 SdkWideRows {
55 /// Conversation receiving the row.
56 conversation_id: ConversationId,
57 /// Signed SDK row limit.
58 limit: u64,
59 /// Current SDK row count.
60 occupied: u64,
61 /// Full requested row count.
62 requested: u64,
63 },
64 /// SDK-wide parked bytes exceeded `D`.
65 SdkWideBytes {
66 /// Conversation receiving the row.
67 conversation_id: ConversationId,
68 /// Signed SDK byte limit.
69 limit: u64,
70 /// Current SDK full-row bytes.
71 occupied: u64,
72 /// Exact full-row bytes requested.
73 requested: u64,
74 },
75}
76
77/// Singleton local counter selector.
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub enum ParkOrderCounter {
80 /// Per-conversation durable park order.
81 ParkOrder,
82}
83
84/// Nonempty parked set exhausted its checked park-order counter.
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub struct SdkParkOrderExhausted {
87 conversation_id: ConversationId,
88}
89
90impl SdkParkOrderExhausted {
91 /// Constructs the only legal exhausted-counter payload.
92 #[must_use]
93 pub const fn new(conversation_id: ConversationId) -> Self {
94 Self { conversation_id }
95 }
96
97 /// Conversation whose nonempty set exhausted.
98 #[must_use]
99 pub const fn conversation_id(self) -> ConversationId {
100 self.conversation_id
101 }
102
103 /// Returns the fixed counter selector.
104 #[must_use]
105 pub const fn counter(self) -> ParkOrderCounter {
106 let _ = self;
107 ParkOrderCounter::ParkOrder
108 }
109
110 /// Returns the terminal maximum counter value.
111 #[must_use]
112 pub const fn value(self) -> u64 {
113 let _ = self;
114 u64::MAX
115 }
116}
117
118/// Singleton ambiguous local operation selector.
119#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120pub enum RecordAdmissionOperation {
121 /// Untokenized ordinary record admission.
122 OrdinaryRecordAdmission,
123}
124
125/// SDK-local terminal ambiguity after an ordinary record response is lost.
126#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub struct RecordAdmissionUnknown {
128 /// Conversation from the lost request.
129 pub conversation_id: ConversationId,
130 /// Participant from the lost request.
131 pub participant_id: ParticipantId,
132 /// Presented generation from the lost request.
133 pub capability_generation: Generation,
134 /// Operation is exactly ordinary record admission.
135 pub operation: RecordAdmissionOperation,
136 /// Durable SDK row order deleted by this transition.
137 pub park_order: u64,
138}
139
140/// SDK terminal state after a credential-rotation result becomes unrecoverable.
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142pub struct CredentialRecoveryLost {
143 /// Conversation whose credential continuity was lost.
144 pub conversation_id: ConversationId,
145 /// Preserved permanent participant identity.
146 pub participant_id: ParticipantId,
147 /// Last credential generation durably known by the SDK.
148 pub last_known_generation: Generation,
149}
150
151/// Reconnect lifecycle state reported without exposing permit identity.
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
153pub enum ReconnectState {
154 /// No authorization or attempt is outstanding.
155 Parked,
156 /// One fresh-event permit is outstanding.
157 PermitOutstanding,
158 /// One real connection attempt is in progress.
159 AttemptInProgress,
160 /// The connection is proved online.
161 Online,
162}
163
164/// Fresh event class required to mint one reconnect permit.
165#[derive(Clone, Copy, Debug, PartialEq, Eq)]
166pub enum ReconnectRequiredEvent {
167 /// Established-connection transport fate.
168 TransportFate,
169 /// Proved online transition.
170 OnlineTransition,
171 /// Explicit caller action.
172 ExplicitCallerAction,
173}
174
175/// Exact SDK-local reconnect result, retained under its stable name.
176///
177/// The former delay field is deliberately absent: fresh events authorize one
178/// real attempt and never a timer arm.
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
180pub enum ReconnectDelayResult {
181 /// A fresh single-use event permit was minted.
182 ReconnectArmed {
183 /// Exact fresh event class that minted the permit.
184 event: ReconnectRequiredEvent,
185 },
186 /// No fresh event permit was minted.
187 ReconnectNotArmed {
188 /// Current reconnect state.
189 state: ReconnectState,
190 /// Event class required for a future authorization.
191 required_event: ReconnectRequiredEvent,
192 },
193}