frame-state 0.2.0

Content-addressed state layer — haematite integration, entities, branching, cross-component references
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
use std::error::Error;
use std::fmt;

use frame_core::capability::{CapabilityCheckError, CapabilityDenied};
use frame_core::component::ComponentId;
use haematite::{
    BranchCommitError, BranchError, BranchKind, BranchPolicyError, BranchRefError, CheckoutError,
    MergeError, NodeError, SnapshotError, StoreError, TreeError,
};

use crate::types::{MergePolicy, ReferenceTargetState};

/// A durable branch/meta mismatch that boot reconciliation refuses to guess at.
#[derive(Debug)]
pub enum ReconcileInconsistency {
    /// A component Namespace ref exists with no corresponding meta record.
    BranchWithoutMeta {
        /// Durable branch name observed.
        branch: String,
        /// Persisted kind observed.
        kind: BranchKind,
    },
    /// Meta says Active but the required namespace ref is absent.
    ActiveWithoutBranch {
        /// Component named by the durable Active record.
        component: ComponentId,
    },
    /// Meta says Archived but a same-named namespace ref is present.
    ArchivedWithBranch {
        /// Component named by both conflicting durable records.
        component: ComponentId,
    },
    /// A removed namespace has no deterministic archive snapshot.
    RemovedWithoutSnapshot {
        /// Component whose branch was removed.
        component: ComponentId,
        /// Snapshot name that must have been pinned first.
        snapshot: String,
    },
    /// A deterministic snapshot name points at a different root.
    SnapshotRootMismatch {
        /// Snapshot name whose existing binding was inspected.
        snapshot: String,
        /// Root required by the archive transition.
        expected: haematite::Hash,
        /// Root already bound under that name.
        actual: haematite::Hash,
    },
}

impl fmt::Display for ReconcileInconsistency {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::BranchWithoutMeta { branch, kind } => write!(
                formatter,
                "durable branch '{branch}' ({kind}) has no frame-state meta record"
            ),
            Self::ActiveWithoutBranch { component } => {
                write!(
                    formatter,
                    "active component {component} has no Namespace branch"
                )
            }
            Self::ArchivedWithBranch { component } => write!(
                formatter,
                "archived component {component} still has a Namespace branch"
            ),
            Self::RemovedWithoutSnapshot {
                component,
                snapshot,
            } => write!(
                formatter,
                "component {component} branch was removed without required snapshot '{snapshot}'"
            ),
            Self::SnapshotRootMismatch {
                snapshot,
                expected,
                actual,
            } => write!(
                formatter,
                "snapshot '{snapshot}' names root {actual}, expected {expected}"
            ),
        }
    }
}

impl Error for ReconcileInconsistency {}

/// Typed failures from component storage, preserving every substrate error.
#[derive(Debug)]
pub enum StateError {
    /// Filesystem setup failed outside a haematite operation.
    Io(std::io::Error),
    /// Node-store operation failed.
    Store(StoreError),
    /// Empty-node construction failed.
    Node(NodeError),
    /// Tree traversal failed.
    Tree(TreeError),
    /// Branch commit/lifecycle failed outside a more specific category.
    BranchCommit(BranchCommitError),
    /// Engine namespace policy refused an operation, retaining names and kinds.
    BranchPolicy(BranchPolicyError),
    /// Engine content merge failed.
    Merge(MergeError),
    /// Durable branch-ref operation failed.
    BranchRef(BranchRefError),
    /// Branch handle operation failed.
    Branch(BranchError),
    /// Snapshot registry operation failed.
    Snapshot(SnapshotError),
    /// Historical checkout failed.
    Checkout(CheckoutError),
    /// The supplied checker belongs to a different component than the handle.
    CapabilityBinding {
        /// Component incarnation represented by the storage handle.
        handle_component: ComponentId,
        /// Component whose authority the supplied checker would evaluate.
        checker_component: ComponentId,
    },
    /// A fresh check denied this resolution, preserved verbatim from frame-core.
    CapabilityDenied(CapabilityDenied),
    /// A fresh check could not produce or publish its verdict.
    CapabilityCheck(CapabilityCheckError),
    /// The named target has no active namespace to resolve.
    DanglingReference {
        /// Component named by the link.
        target: ComponentId,
        /// Durable target state observed after authority was granted.
        state: ReferenceTargetState,
    },
    /// Internal synchronization was poisoned by a prior panic.
    SynchronizationPoisoned,
    /// No schema was declared before install.
    SchemaNotDeclared {
        /// Component whose install lacks a declaration.
        component: ComponentId,
    },
    /// A schema was already fixed for this component's current lifecycle.
    SchemaAlreadyDeclared {
        /// Component whose pending declaration would be replaced.
        component: ComponentId,
    },
    /// The published engine cannot execute the declared vocabulary entry.
    UnsupportedPolicy {
        /// Policy refused at schema declaration.
        policy: MergePolicy,
    },
    /// A custom policy named a resolver absent at declaration time.
    ResolverNotRegistered {
        /// Stable resolver name that was not registered.
        name: String,
    },
    /// A resolver registration would silently replace an existing function.
    ResolverAlreadyRegistered {
        /// Stable resolver name already bound in this process.
        name: String,
    },
    /// An install is already executing for this identity.
    InstallInProgress {
        /// Component with an active install transition.
        component: ComponentId,
    },
    /// An archive is already executing for this identity.
    ArchiveInProgress {
        /// Component with an active archive transition.
        component: ComponentId,
    },
    /// A fork or merge is already executing for this identity.
    WorkInProgress {
        /// Component with an active Work operation.
        component: ComponentId,
    },
    /// Install was requested for an active namespace.
    AlreadyActive {
        /// Component whose namespace is already Active.
        component: ComponentId,
    },
    /// The component has no active namespace.
    NotActive {
        /// Component for which active storage was required.
        component: ComponentId,
    },
    /// A handle's captured incarnation no longer owns the branch name.
    StaleHandle {
        /// Component whose name was reused.
        component: ComponentId,
        /// Incarnation captured by the handle.
        held: u64,
        /// Durable current incarnation.
        current: u64,
    },
    /// A requested Work branch is not outstanding.
    WorkNotFound {
        /// Durable Work branch name that was absent.
        branch: String,
    },
    /// A Work handle came from another store, component, or incarnation.
    ForeignWorkHandle,
    /// A branch/archive key did not have Frame's fixed-width encoding.
    CorruptRecord {
        /// Exact codec or durable-invariant failure.
        detail: String,
    },
    /// Boot found a durable state it cannot safely repair.
    Reconcile(ReconcileInconsistency),
}

impl fmt::Display for StateError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(error) => write!(formatter, "frame-state filesystem error: {error}"),
            Self::Store(error) => write!(formatter, "frame-state node store error: {error}"),
            Self::Node(error) => write!(formatter, "frame-state node encoding error: {error}"),
            Self::Tree(error) => write!(formatter, "frame-state tree traversal error: {error}"),
            Self::BranchCommit(error) => error.fmt(formatter),
            Self::BranchPolicy(error) => error.fmt(formatter),
            Self::Merge(error) => error.fmt(formatter),
            Self::BranchRef(error) => error.fmt(formatter),
            Self::Branch(error) => error.fmt(formatter),
            Self::Snapshot(error) => error.fmt(formatter),
            Self::Checkout(error) => error.fmt(formatter),
            Self::CapabilityBinding {
                handle_component,
                checker_component,
            } => fmt_capability_binding(formatter, *handle_component, *checker_component),
            Self::CapabilityDenied(denial) => fmt_capability_denied(formatter, denial),
            Self::CapabilityCheck(error) => error.fmt(formatter),
            Self::DanglingReference { target, state } => {
                fmt_dangling_reference(formatter, *target, *state)
            }
            Self::SynchronizationPoisoned => {
                formatter.write_str("frame-state synchronization poisoned")
            }
            Self::SchemaNotDeclared { component } => {
                write!(
                    formatter,
                    "component {component} has no declared storage schema"
                )
            }
            Self::SchemaAlreadyDeclared { component } => {
                write!(
                    formatter,
                    "component {component} storage schema is already declared"
                )
            }
            Self::UnsupportedPolicy { policy } => {
                write!(
                    formatter,
                    "haematite 0.5.0 cannot execute merge policy {policy}"
                )
            }
            Self::ResolverNotRegistered { name } => {
                write!(
                    formatter,
                    "custom merge resolver '{name}' is not registered"
                )
            }
            Self::ResolverAlreadyRegistered { name } => {
                write!(
                    formatter,
                    "custom merge resolver '{name}' is already registered"
                )
            }
            Self::InstallInProgress { component } => {
                write!(
                    formatter,
                    "component {component} storage install is in progress"
                )
            }
            Self::ArchiveInProgress { component } => {
                write!(
                    formatter,
                    "component {component} storage archive is in progress"
                )
            }
            Self::WorkInProgress { component } => {
                write!(
                    formatter,
                    "component {component} Work operation is in progress"
                )
            }
            Self::AlreadyActive { component } => {
                write!(formatter, "component {component} storage is already active")
            }
            Self::NotActive { component } => {
                write!(formatter, "component {component} storage is not active")
            }
            Self::StaleHandle {
                component,
                held,
                current,
            } => write!(
                formatter,
                "stale handle for component {component}: held incarnation {held}, current incarnation {current}"
            ),
            Self::WorkNotFound { branch } => {
                write!(formatter, "Work branch '{branch}' is not outstanding")
            }
            Self::ForeignWorkHandle => formatter
                .write_str("Work handle belongs to another store, component, or incarnation"),
            Self::CorruptRecord { detail } => {
                write!(formatter, "corrupt frame-state record: {detail}")
            }
            Self::Reconcile(error) => error.fmt(formatter),
        }
    }
}

fn fmt_capability_binding(
    formatter: &mut fmt::Formatter<'_>,
    handle_component: ComponentId,
    checker_component: ComponentId,
) -> fmt::Result {
    write!(
        formatter,
        "capability checker for component {checker_component} cannot resolve through component {handle_component}'s storage handle"
    )
}

fn fmt_capability_denied(
    formatter: &mut fmt::Formatter<'_>,
    denial: &CapabilityDenied,
) -> fmt::Result {
    write!(
        formatter,
        "cross-component resolution denied for component {}: kind {:?}, scope {:?}, declared {}",
        denial.component_id, denial.kind, denial.scope, denial.declared
    )
}

fn fmt_dangling_reference(
    formatter: &mut fmt::Formatter<'_>,
    target: ComponentId,
    state: ReferenceTargetState,
) -> fmt::Result {
    write!(
        formatter,
        "cross-component reference target {target} is {state}"
    )
}

impl Error for StateError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::Io(error) => Some(error),
            Self::Store(error) => Some(error),
            Self::Node(error) => Some(error),
            Self::Tree(error) => Some(error),
            Self::BranchCommit(error) => Some(error),
            Self::BranchPolicy(error) => Some(error),
            Self::Merge(error) => Some(error),
            Self::BranchRef(error) => Some(error),
            Self::Branch(error) => Some(error),
            Self::Snapshot(error) => Some(error),
            Self::Checkout(error) => Some(error),
            Self::CapabilityCheck(error) => Some(error),
            Self::Reconcile(error) => Some(error),
            _ => None,
        }
    }
}

impl From<std::io::Error> for StateError {
    fn from(error: std::io::Error) -> Self {
        Self::Io(error)
    }
}
impl From<StoreError> for StateError {
    fn from(error: StoreError) -> Self {
        Self::Store(error)
    }
}
impl From<NodeError> for StateError {
    fn from(error: NodeError) -> Self {
        Self::Node(error)
    }
}
impl From<TreeError> for StateError {
    fn from(error: TreeError) -> Self {
        Self::Tree(error)
    }
}
impl From<BranchRefError> for StateError {
    fn from(error: BranchRefError) -> Self {
        Self::BranchRef(error)
    }
}
impl From<BranchError> for StateError {
    fn from(error: BranchError) -> Self {
        Self::Branch(error)
    }
}
impl From<SnapshotError> for StateError {
    fn from(error: SnapshotError) -> Self {
        Self::Snapshot(error)
    }
}
impl From<CheckoutError> for StateError {
    fn from(error: CheckoutError) -> Self {
        Self::Checkout(error)
    }
}
impl From<CapabilityCheckError> for StateError {
    fn from(error: CapabilityCheckError) -> Self {
        Self::CapabilityCheck(error)
    }
}
impl From<ReconcileInconsistency> for StateError {
    fn from(error: ReconcileInconsistency) -> Self {
        Self::Reconcile(error)
    }
}

impl From<BranchCommitError> for StateError {
    fn from(error: BranchCommitError) -> Self {
        match error {
            BranchCommitError::Policy(policy) => Self::BranchPolicy(policy),
            BranchCommitError::Merge(merge) => Self::Merge(merge),
            other => Self::BranchCommit(other),
        }
    }
}