@feltdb/core 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +26 -1
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +465 -107
- package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +96 -0
- package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +191 -0
- package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +1153 -0
- package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +771 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +3 -3
- package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1 -1
- package/dist/http-db.d.ts +11 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +11 -3
- package/dist/studio-app/assets/{index-Bbos1m2U.js → index-ByHX4xDq.js} +1 -1
- package/dist/studio-app/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
//! The FeltDB production contract.
|
|
2
|
+
//!
|
|
3
|
+
//! The certification matrix records what the repository *proves*. This file
|
|
4
|
+
//! records what FeltDB is willing to *promise*, which is a smaller and
|
|
5
|
+
//! different thing: a promise has an audience, and an audience reads a
|
|
6
|
+
//! guarantee as unconditional unless the condition is written next to it.
|
|
7
|
+
//!
|
|
8
|
+
//! # Why this is executable
|
|
9
|
+
//!
|
|
10
|
+
//! A public contract is the natural place for evidence drift to end up. Nobody
|
|
11
|
+
//! edits a matrix to make a sale; people edit a promise. So no promise here
|
|
12
|
+
//! stands on its own wording — every one names the certification claims that
|
|
13
|
+
//! back it, and the tier it may occupy is *derived* from their statuses:
|
|
14
|
+
//!
|
|
15
|
+
//! - **Certified** — every backing claim is `Proven`. Promised without
|
|
16
|
+
//! qualification.
|
|
17
|
+
//! - **Conditional** — every backing claim is `Proven`, and the promise states
|
|
18
|
+
//! the condition it holds under. Fully proven, deliberately scoped.
|
|
19
|
+
//! - **Not certified** — at least one backing claim is not `Proven`. Written
|
|
20
|
+
//! down as a thing FeltDB does not promise, with the evidence that would
|
|
21
|
+
//! change that.
|
|
22
|
+
//!
|
|
23
|
+
//! Promoting a promise therefore requires promoting a claim, which requires a
|
|
24
|
+
//! test. There is no path from wanting a guarantee to publishing one.
|
|
25
|
+
//!
|
|
26
|
+
//! # What this is not
|
|
27
|
+
//!
|
|
28
|
+
//! It is not a second source of truth. Statuses are read out of
|
|
29
|
+
//! `production_certification.rs`; nothing about a claim is restated here.
|
|
30
|
+
|
|
31
|
+
use std::collections::{BTreeMap, BTreeSet};
|
|
32
|
+
|
|
33
|
+
mod common;
|
|
34
|
+
|
|
35
|
+
/// How strongly FeltDB stands behind a promise.
|
|
36
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
37
|
+
enum Tier {
|
|
38
|
+
Certified,
|
|
39
|
+
Conditional,
|
|
40
|
+
NotCertified,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
impl Tier {
|
|
44
|
+
const ALL: [Tier; 3] = [Tier::Certified, Tier::Conditional, Tier::NotCertified];
|
|
45
|
+
|
|
46
|
+
fn label(self) -> &'static str {
|
|
47
|
+
match self {
|
|
48
|
+
Tier::Certified => "Certified",
|
|
49
|
+
Tier::Conditional => "Conditional",
|
|
50
|
+
Tier::NotCertified => "Not certified",
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// A tier that promises something, as opposed to recording that FeltDB does
|
|
55
|
+
/// not. The distinction decides which checks apply.
|
|
56
|
+
fn promises(self) -> bool {
|
|
57
|
+
matches!(self, Tier::Certified | Tier::Conditional)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
struct Promise {
|
|
62
|
+
id: &'static str,
|
|
63
|
+
area: &'static str,
|
|
64
|
+
promise: &'static str,
|
|
65
|
+
tier: Tier,
|
|
66
|
+
/// For `Conditional`, the condition the promise holds under. For
|
|
67
|
+
/// `Not certified`, the evidence that would change the answer. Empty only
|
|
68
|
+
/// for `Certified`, which holds unconditionally or not at all.
|
|
69
|
+
condition: &'static str,
|
|
70
|
+
/// The certification claims this rests on. Never empty.
|
|
71
|
+
backing: &'static [&'static str],
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#[rustfmt::skip]
|
|
75
|
+
const CONTRACT: &[Promise] = &[
|
|
76
|
+
// ------------------------------------------------------------- Certified
|
|
77
|
+
Promise { id: "P1", area: "Durability",
|
|
78
|
+
promise: "State, revision history, retention policy and retention horizon survive a clean \
|
|
79
|
+
restart, and a reopened database continues its history rather than starting a new one.",
|
|
80
|
+
tier: Tier::Certified, condition: "", backing: &["D1", "D2", "D3", "D4"] },
|
|
81
|
+
Promise { id: "P2", area: "Mutation",
|
|
82
|
+
promise: "A write that has lost its fence is rejected and mints no revision, and concurrent \
|
|
83
|
+
writes produce correct per-resource revision chains.",
|
|
84
|
+
tier: Tier::Certified, condition: "", backing: &["A1", "A2", "A3"] },
|
|
85
|
+
Promise { id: "P3", area: "Mutation",
|
|
86
|
+
promise: "A duplicate remote operation is idempotent, and a remote operation arriving with a \
|
|
87
|
+
sequence gap is rejected rather than applied.",
|
|
88
|
+
tier: Tier::Certified, condition: "", backing: &["A4", "A5"] },
|
|
89
|
+
Promise { id: "P4", area: "Crash recovery",
|
|
90
|
+
promise: "A crash at any point leaves a clean prefix of the mutation sequence: never a partial \
|
|
91
|
+
mutation, and never a revision for a record that was not written.",
|
|
92
|
+
tier: Tier::Certified, condition: "", backing: &["A6", "D6", "D7"] },
|
|
93
|
+
Promise { id: "P5", area: "Crash recovery",
|
|
94
|
+
promise: "A transaction is all-or-nothing across a crash.",
|
|
95
|
+
tier: Tier::Certified, condition: "", backing: &["D9"] },
|
|
96
|
+
Promise { id: "P6", area: "Crash recovery",
|
|
97
|
+
promise: "A committed write survives the process dying.",
|
|
98
|
+
tier: Tier::Certified, condition: "", backing: &["D8"] },
|
|
99
|
+
Promise { id: "P7", area: "Crash recovery",
|
|
100
|
+
promise: "A database recovered from a crash replicates identically to one that never crashed.",
|
|
101
|
+
tier: Tier::Certified, condition: "", backing: &["D10"] },
|
|
102
|
+
Promise { id: "P8", area: "History",
|
|
103
|
+
promise: "History is bounded by a retention policy and unbounded without one, a policy change \
|
|
104
|
+
takes effect in both directions, maintenance never rewrites history, and forked \
|
|
105
|
+
history is preserved.",
|
|
106
|
+
tier: Tier::Certified, condition: "", backing: &["H1", "H2", "H3", "H4", "H5"] },
|
|
107
|
+
Promise { id: "P9", area: "History",
|
|
108
|
+
promise: "Ancestry expired by retention is distinguishable from ancestry that was damaged.",
|
|
109
|
+
tier: Tier::Certified, condition: "", backing: &["R3"] },
|
|
110
|
+
Promise { id: "P10", area: "Corruption",
|
|
111
|
+
promise: "An incomplete final append is recovered and reported rather than silently dropped, \
|
|
112
|
+
and a complete but invalid record refuses the open wherever it sits.",
|
|
113
|
+
tier: Tier::Certified, condition: "", backing: &["R1", "R2"] },
|
|
114
|
+
Promise { id: "P11", area: "Format",
|
|
115
|
+
promise: "A durable database declares its format, and a format this build cannot interpret \
|
|
116
|
+
refuses the open before any record is read, leaving the file byte-identical.",
|
|
117
|
+
tier: Tier::Certified, condition: "", backing: &["U1", "U3", "U4", "U5", "U6"] },
|
|
118
|
+
Promise { id: "P12", area: "Backup",
|
|
119
|
+
promise: "A backup is self-contained and independently verifiable, a restore proves the result \
|
|
120
|
+
means the same thing rather than merely that it parsed, an altered backup is refused, \
|
|
121
|
+
and a restore never overwrites an existing database.",
|
|
122
|
+
tier: Tier::Certified, condition: "", backing: &["B1", "B2", "B3", "B4"] },
|
|
123
|
+
Promise { id: "P13", area: "Replication",
|
|
124
|
+
promise: "Replication carries revision history with identical identities, preserving parents, \
|
|
125
|
+
sequences, forks and provenance, and evaluates state equivalence separately from \
|
|
126
|
+
historical equivalence.",
|
|
127
|
+
tier: Tier::Certified, condition: "", backing: &["S1", "S2", "S3", "S4", "S5"] },
|
|
128
|
+
Promise { id: "P14", area: "Overload",
|
|
129
|
+
promise: "A request arriving while the server is saturated is refused with a status, an error \
|
|
130
|
+
code and a retry hint, rather than absorbed into an unbounded queue.",
|
|
131
|
+
tier: Tier::Certified, condition: "", backing: &["M4"] },
|
|
132
|
+
Promise { id: "P15", area: "Reads",
|
|
133
|
+
promise: "A keyed lookup reads one record, and a limited search visits no more rows than its limit.",
|
|
134
|
+
tier: Tier::Certified, condition: "", backing: &["M5"] },
|
|
135
|
+
Promise { id: "P16", area: "Maintenance",
|
|
136
|
+
promise: "A timer-driven compaction prunes without rewriting the durable log, an explicit \
|
|
137
|
+
compaction still rewrites immediately, and deferring the rewrite preserves state and history.",
|
|
138
|
+
tier: Tier::Certified, condition: "", backing: &["M1", "M2", "M3"] },
|
|
139
|
+
Promise { id: "P17", area: "Maintenance",
|
|
140
|
+
promise: "A compaction running concurrently with traffic leaves scoped reads correct and bounded, \
|
|
141
|
+
and leaves the database intact across a forced rewrite and a reopen.",
|
|
142
|
+
tier: Tier::Certified, condition: "", backing: &["M8", "M9"] },
|
|
143
|
+
Promise { id: "P18", area: "Health",
|
|
144
|
+
promise: "A health report names only conditions that were observed, and a recovered open is \
|
|
145
|
+
distinguishable from a clean one.",
|
|
146
|
+
tier: Tier::Certified, condition: "", backing: &["O3", "O4"] },
|
|
147
|
+
|
|
148
|
+
// ----------------------------------------------------------- Conditional
|
|
149
|
+
Promise { id: "P19", area: "Durability mode",
|
|
150
|
+
promise: "`Flushed`, the default, hands the write to the operating system and states that as its guarantee.",
|
|
151
|
+
tier: Tier::Conditional,
|
|
152
|
+
condition: "The write survives the process dying. It is not promised to survive the machine \
|
|
153
|
+
losing power.",
|
|
154
|
+
backing: &["D8", "E3", "E5"] },
|
|
155
|
+
Promise { id: "P20", area: "Durability mode",
|
|
156
|
+
promise: "`Synced` issues a stable-storage barrier for every write, at a cost this repository has measured.",
|
|
157
|
+
tier: Tier::Conditional,
|
|
158
|
+
condition: "The barrier is issued. What the hardware does with it is outside FeltDB's evidence, \
|
|
159
|
+
so this narrows the power-loss window rather than closing it.",
|
|
160
|
+
backing: &["E3", "E4", "E5"] },
|
|
161
|
+
Promise { id: "P21", area: "Durability mode",
|
|
162
|
+
promise: "`Grouped` names an exact bound on how many acknowledged writes have not yet been \
|
|
163
|
+
through a barrier.",
|
|
164
|
+
tier: Tier::Conditional,
|
|
165
|
+
condition: "Up to `every - 1` acknowledged writes may be in that window. Choose this mode for \
|
|
166
|
+
the stated window, not for throughput.",
|
|
167
|
+
backing: &["E3", "E5"] },
|
|
168
|
+
Promise { id: "P22", area: "Workload",
|
|
169
|
+
promise: "Retention cost per write grows with the retention window, and retention inflates the \
|
|
170
|
+
log before compaction.",
|
|
171
|
+
tier: Tier::Conditional,
|
|
172
|
+
condition: "Under the frozen workload in the envelope contract. The ordering is the promise; \
|
|
173
|
+
the measured numbers are not.",
|
|
174
|
+
backing: &["E1", "E2"] },
|
|
175
|
+
Promise { id: "P23", area: "Durability mode",
|
|
176
|
+
promise: "Selecting a durability mode changes the contract and not the stored database, so a \
|
|
177
|
+
mode can be changed without migrating anything.",
|
|
178
|
+
tier: Tier::Conditional,
|
|
179
|
+
condition: "The stored bytes are identical across modes; the acknowledgement semantics are not.",
|
|
180
|
+
backing: &["E5"] },
|
|
181
|
+
|
|
182
|
+
// --------------------------------------------------------- Not certified
|
|
183
|
+
Promise { id: "N1", area: "Durability",
|
|
184
|
+
promise: "FeltDB does not promise that a single write survives operating-system or power loss.",
|
|
185
|
+
tier: Tier::NotCertified,
|
|
186
|
+
condition: "A test on hardware that can actually be cut, verifying the database after recovery.",
|
|
187
|
+
backing: &["D5"] },
|
|
188
|
+
Promise { id: "N2", area: "Workload",
|
|
189
|
+
promise: "FeltDB does not promise that `Grouped` is faster than a barrier per write.",
|
|
190
|
+
tier: Tier::NotCertified,
|
|
191
|
+
condition: "A stable measurement showing the ordering holds across repeated runs. An earlier \
|
|
192
|
+
assertion was removed after a run inverted it.",
|
|
193
|
+
backing: &["E7"] },
|
|
194
|
+
Promise { id: "N3", area: "Workload",
|
|
195
|
+
promise: "FeltDB does not promise that its published performance numbers transfer to your hardware.",
|
|
196
|
+
tier: Tier::NotCertified,
|
|
197
|
+
condition: "A test asserting that each measurement carries the environment that produced it, \
|
|
198
|
+
as the frozen workload already is.",
|
|
199
|
+
backing: &["E6"] },
|
|
200
|
+
Promise { id: "N4", area: "Backup",
|
|
201
|
+
promise: "FeltDB does not provide an operator backup workflow. Backup and restore are library \
|
|
202
|
+
primitives that something else must schedule and run.",
|
|
203
|
+
tier: Tier::NotCertified,
|
|
204
|
+
condition: "A command-line entry point with a contract test of its own.",
|
|
205
|
+
backing: &["B5"] },
|
|
206
|
+
Promise { id: "N5", area: "Monitoring",
|
|
207
|
+
promise: "FeltDB does not surface ancestry damage on its own. Damage is found when ancestry is \
|
|
208
|
+
walked, and nothing walks it unprompted.",
|
|
209
|
+
tier: Tier::NotCertified,
|
|
210
|
+
condition: "A database-wide ancestry scan, and something that runs it on a schedule.",
|
|
211
|
+
backing: &["R4"] },
|
|
212
|
+
Promise { id: "N6", area: "Inspection",
|
|
213
|
+
promise: "FeltDB does not answer database-wide questions about history. Inspection is per resource.",
|
|
214
|
+
tier: Tier::NotCertified,
|
|
215
|
+
condition: "An aggregate across resources, so that \"is any ancestry anywhere damaged?\" does \
|
|
216
|
+
not mean walking every revision.",
|
|
217
|
+
backing: &["O1"] },
|
|
218
|
+
Promise { id: "N7", area: "Health",
|
|
219
|
+
promise: "FeltDB does not certify the behaviour of its HTTP health and readiness endpoints. The \
|
|
220
|
+
values they report are tested at the library boundary; the handlers that compose them \
|
|
221
|
+
are not exercised.",
|
|
222
|
+
tier: Tier::NotCertified,
|
|
223
|
+
condition: "An HTTP-level harness covering both handlers, including the stalled-storage path.",
|
|
224
|
+
backing: &["O2", "M7"] },
|
|
225
|
+
Promise { id: "N8", area: "Health",
|
|
226
|
+
promise: "The `runtime`, `fabric`, `workflows` and `agents` fields of a health response are \
|
|
227
|
+
compile-time constants and carry no information about the running system.",
|
|
228
|
+
tier: Tier::NotCertified,
|
|
229
|
+
condition: "An operational contract for each field, and then observation of it. Inventing those \
|
|
230
|
+
contracts to fill the fields would repeat the defect being recorded.",
|
|
231
|
+
backing: &["O5"] },
|
|
232
|
+
Promise { id: "N9", area: "Operations",
|
|
233
|
+
promise: "FeltDB does not promise a latency bound, and does not promise that a compaction will \
|
|
234
|
+
not block concurrent requests. Reads and writes are serialized behind a single lock, \
|
|
235
|
+
the durable rewrite is deferred rather than eliminated, and no test in this repository \
|
|
236
|
+
measures latency. That is a statement about the available evidence, not a prediction \
|
|
237
|
+
about any particular deployment.",
|
|
238
|
+
tier: Tier::NotCertified,
|
|
239
|
+
condition: "Latency measured under load, and a compaction that does not hold the lock that \
|
|
240
|
+
serializes every read and write.",
|
|
241
|
+
backing: &["M6"] },
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
/// What an operator has to do because FeltDB does not.
|
|
245
|
+
///
|
|
246
|
+
/// Every withheld claim lands here as work someone else owns. A gap that
|
|
247
|
+
/// produced no obligation would be a gap nobody had to plan around, which is
|
|
248
|
+
/// how a stated limitation quietly becomes an operational surprise.
|
|
249
|
+
struct Obligation {
|
|
250
|
+
id: &'static str,
|
|
251
|
+
obligation: &'static str,
|
|
252
|
+
because: &'static [&'static str],
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#[rustfmt::skip]
|
|
256
|
+
const OBLIGATIONS: &[Obligation] = &[
|
|
257
|
+
Obligation { id: "G1",
|
|
258
|
+
obligation: "If the deployment can lose power, treat the most recent acknowledged writes as at \
|
|
259
|
+
risk and size recovery accordingly. `Synced` narrows that window; nothing in this \
|
|
260
|
+
repository closes it.",
|
|
261
|
+
because: &["D5"] },
|
|
262
|
+
Obligation { id: "G2",
|
|
263
|
+
obligation: "Schedule, run and retain backups yourself, and keep the verification step: FeltDB \
|
|
264
|
+
will tell you a backup is sound, but only when asked.",
|
|
265
|
+
because: &["B5"] },
|
|
266
|
+
Obligation { id: "G3",
|
|
267
|
+
obligation: "Walk ancestry on a schedule if damage needs to be found before a reader stumbles on it.",
|
|
268
|
+
because: &["R4"] },
|
|
269
|
+
Obligation { id: "G4",
|
|
270
|
+
obligation: "Aggregate per-resource history yourself when the question is about the database \
|
|
271
|
+
rather than about one resource.",
|
|
272
|
+
because: &["O1"] },
|
|
273
|
+
Obligation { id: "G5",
|
|
274
|
+
obligation: "Re-measure the workload envelope on the hardware you intend to run on, before sizing anything.",
|
|
275
|
+
because: &["E6"] },
|
|
276
|
+
Obligation { id: "G6",
|
|
277
|
+
obligation: "Do not select `Grouped` for throughput. Select it for the window it states.",
|
|
278
|
+
because: &["E7"] },
|
|
279
|
+
Obligation { id: "G7",
|
|
280
|
+
obligation: "Monitor request latency and refusal rate outside the database. FeltDB reports \
|
|
281
|
+
neither, and a saturated instance answers `/health` normally.",
|
|
282
|
+
because: &["M6"] },
|
|
283
|
+
Obligation { id: "G8",
|
|
284
|
+
obligation: "Treat the HTTP health and readiness responses as unverified composition. Where \
|
|
285
|
+
storage state matters, confirm it at the library boundary.",
|
|
286
|
+
because: &["O2", "M7"] },
|
|
287
|
+
Obligation { id: "G9",
|
|
288
|
+
obligation: "Ignore the `runtime`, `fabric`, `workflows` and `agents` fields of a health response.",
|
|
289
|
+
because: &["O5"] },
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
/// Certification claims that deliberately reach no promise, and why.
|
|
293
|
+
///
|
|
294
|
+
/// Enumerated rather than filtered, so the exemption cannot grow silently.
|
|
295
|
+
const OUTSIDE_THE_CONTRACT: &[(&str, &str)] = &[(
|
|
296
|
+
"U2",
|
|
297
|
+
"Not Applicable: there is no pre-model revision history to migrate, so there is nothing to \
|
|
298
|
+
promise or withhold about migrating it.",
|
|
299
|
+
)];
|
|
300
|
+
|
|
301
|
+
// ===========================================================================
|
|
302
|
+
// The certification, read rather than restated
|
|
303
|
+
// ===========================================================================
|
|
304
|
+
|
|
305
|
+
struct Claim {
|
|
306
|
+
status: String,
|
|
307
|
+
kind: String,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/// Parse the certification matrix out of its own source.
|
|
311
|
+
///
|
|
312
|
+
/// Reading the Rust rather than the published document is deliberate: the
|
|
313
|
+
/// document is checked against the matrix by a different test binary, and a
|
|
314
|
+
/// contract that depended on that check would silently lose its footing if the
|
|
315
|
+
/// check were ever removed.
|
|
316
|
+
fn certification() -> BTreeMap<String, Claim> {
|
|
317
|
+
let source = include_str!("production_certification.rs");
|
|
318
|
+
let start = source
|
|
319
|
+
.find("const CERTIFICATION")
|
|
320
|
+
.expect("the certification declares CERTIFICATION");
|
|
321
|
+
let end = start
|
|
322
|
+
+ source[start..]
|
|
323
|
+
.find("\n];")
|
|
324
|
+
.expect("the certification table terminates");
|
|
325
|
+
let table = &source[start..end];
|
|
326
|
+
|
|
327
|
+
let mut claims = BTreeMap::new();
|
|
328
|
+
for chunk in table.split("Claim {").skip(1) {
|
|
329
|
+
let id = quoted_after(chunk, "id: \"").expect("every claim has an id");
|
|
330
|
+
let status = word_after(chunk, "status: Status::").expect("every claim has a status");
|
|
331
|
+
let kind = word_after(chunk, "kind: Evidence::").expect("every claim has an evidence kind");
|
|
332
|
+
assert!(
|
|
333
|
+
claims
|
|
334
|
+
.insert(id.to_string(), Claim { status, kind })
|
|
335
|
+
.is_none(),
|
|
336
|
+
"{id} appears twice in the certification"
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
assert!(
|
|
340
|
+
claims.len() > 50,
|
|
341
|
+
"the certification did not parse; it yielded {} claims",
|
|
342
|
+
claims.len()
|
|
343
|
+
);
|
|
344
|
+
claims
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
fn quoted_after<'a>(chunk: &'a str, key: &str) -> Option<&'a str> {
|
|
348
|
+
let rest = &chunk[chunk.find(key)? + key.len()..];
|
|
349
|
+
Some(&rest[..rest.find('"')?])
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
fn word_after(chunk: &str, key: &str) -> Option<String> {
|
|
353
|
+
let rest = &chunk[chunk.find(key)? + key.len()..];
|
|
354
|
+
Some(
|
|
355
|
+
rest.chars()
|
|
356
|
+
.take_while(|character| character.is_alphanumeric())
|
|
357
|
+
.collect(),
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
fn contract() -> BTreeMap<&'static str, &'static Promise> {
|
|
362
|
+
CONTRACT
|
|
363
|
+
.iter()
|
|
364
|
+
.map(|promise| (promise.id, promise))
|
|
365
|
+
.collect()
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ===========================================================================
|
|
369
|
+
// The tripwires
|
|
370
|
+
// ===========================================================================
|
|
371
|
+
|
|
372
|
+
/// Every promise rests on claims that exist, and no promise rests on nothing.
|
|
373
|
+
#[test]
|
|
374
|
+
fn every_promise_names_backing_that_exists() {
|
|
375
|
+
let certified = certification();
|
|
376
|
+
let mut problems = Vec::new();
|
|
377
|
+
|
|
378
|
+
for promise in CONTRACT {
|
|
379
|
+
if promise.backing.is_empty() {
|
|
380
|
+
problems.push(format!(
|
|
381
|
+
"{}: promises something and names no claim",
|
|
382
|
+
promise.id
|
|
383
|
+
));
|
|
384
|
+
}
|
|
385
|
+
for id in promise.backing {
|
|
386
|
+
if !certified.contains_key(*id) {
|
|
387
|
+
problems.push(format!(
|
|
388
|
+
"{}: rests on `{id}`, which is not a certification claim",
|
|
389
|
+
promise.id
|
|
390
|
+
));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
assert!(problems.is_empty(), "{}", problems.join("\n"));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/// **A promise may not be stronger than its evidence.**
|
|
399
|
+
///
|
|
400
|
+
/// This is the whole mechanism. A `Certified` or `Conditional` promise requires
|
|
401
|
+
/// every backing claim to be `Proven`; a `Not certified` entry requires at least
|
|
402
|
+
/// one that is not. Wanting to promise something more is not a way to promise
|
|
403
|
+
/// it — the only route is to prove a claim, which means writing a test.
|
|
404
|
+
#[test]
|
|
405
|
+
fn a_promises_tier_is_derived_from_its_evidence() {
|
|
406
|
+
let certified = certification();
|
|
407
|
+
let mut problems = Vec::new();
|
|
408
|
+
|
|
409
|
+
for promise in CONTRACT {
|
|
410
|
+
let statuses: Vec<&str> = promise
|
|
411
|
+
.backing
|
|
412
|
+
.iter()
|
|
413
|
+
.filter_map(|id| certified.get(*id))
|
|
414
|
+
.map(|claim| claim.status.as_str())
|
|
415
|
+
.collect();
|
|
416
|
+
|
|
417
|
+
let unproven: Vec<&&str> = promise
|
|
418
|
+
.backing
|
|
419
|
+
.iter()
|
|
420
|
+
.filter(|id| {
|
|
421
|
+
certified
|
|
422
|
+
.get(**id)
|
|
423
|
+
.map(|claim| claim.status != "Proven")
|
|
424
|
+
.unwrap_or(false)
|
|
425
|
+
})
|
|
426
|
+
.collect();
|
|
427
|
+
|
|
428
|
+
match promise.tier {
|
|
429
|
+
Tier::Certified | Tier::Conditional if !unproven.is_empty() => problems.push(format!(
|
|
430
|
+
"{} is published as `{}` and rests on {unproven:?}, which {} not Proven",
|
|
431
|
+
promise.id,
|
|
432
|
+
promise.tier.label(),
|
|
433
|
+
if unproven.len() == 1 { "is" } else { "are" }
|
|
434
|
+
)),
|
|
435
|
+
Tier::NotCertified if unproven.is_empty() => problems.push(format!(
|
|
436
|
+
"{} is published as `Not certified` and every claim it names is Proven ({statuses:?}); \
|
|
437
|
+
either promise it or point it at the gap",
|
|
438
|
+
promise.id
|
|
439
|
+
)),
|
|
440
|
+
_ => {}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
assert!(problems.is_empty(), "{}", problems.join("\n"));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/// A promise we make must not rest on someone having read the implementation.
|
|
448
|
+
///
|
|
449
|
+
/// `inspection_alone_never_proves_a_behavioural_claim` already stops inspection
|
|
450
|
+
/// from producing a `Proven` claim. This is the same rule one level up, so that
|
|
451
|
+
/// a promise cannot inherit inspection through a chain of ids.
|
|
452
|
+
#[test]
|
|
453
|
+
fn no_promise_rests_on_implementation_inspection() {
|
|
454
|
+
let certified = certification();
|
|
455
|
+
for promise in CONTRACT {
|
|
456
|
+
if !promise.tier.promises() {
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
for id in promise.backing {
|
|
460
|
+
let claim = &certified[*id];
|
|
461
|
+
assert_ne!(
|
|
462
|
+
claim.kind, "ImplementationInspection",
|
|
463
|
+
"{} promises something backed by `{id}`, which rests on inspection",
|
|
464
|
+
promise.id
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/// A conditional promise states its condition; a withheld one states what would
|
|
471
|
+
/// change the answer. Only an unconditional promise may say nothing further.
|
|
472
|
+
#[test]
|
|
473
|
+
fn every_qualified_promise_states_its_qualification() {
|
|
474
|
+
for promise in CONTRACT {
|
|
475
|
+
match promise.tier {
|
|
476
|
+
Tier::Certified => assert!(
|
|
477
|
+
promise.condition.is_empty(),
|
|
478
|
+
"{} is Certified and carries a condition; it belongs in Conditional",
|
|
479
|
+
promise.id
|
|
480
|
+
),
|
|
481
|
+
Tier::Conditional | Tier::NotCertified => assert!(
|
|
482
|
+
!promise.condition.trim().is_empty(),
|
|
483
|
+
"{} is `{}` and states no qualification",
|
|
484
|
+
promise.id,
|
|
485
|
+
promise.tier.label()
|
|
486
|
+
),
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/// **Every claim reaches the contract, or is exempted by name.**
|
|
492
|
+
///
|
|
493
|
+
/// Coverage in the direction that matters: a claim the certification withholds
|
|
494
|
+
/// must not simply be left out of the public document. Silence about a gap
|
|
495
|
+
/// reads as an absence of the gap.
|
|
496
|
+
#[test]
|
|
497
|
+
fn every_certification_claim_reaches_the_contract() {
|
|
498
|
+
let certified = certification();
|
|
499
|
+
let mentioned: BTreeSet<&str> = CONTRACT
|
|
500
|
+
.iter()
|
|
501
|
+
.flat_map(|promise| promise.backing.iter().copied())
|
|
502
|
+
.collect();
|
|
503
|
+
|
|
504
|
+
let mut missing = Vec::new();
|
|
505
|
+
for (id, claim) in &certified {
|
|
506
|
+
if mentioned.contains(id.as_str()) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
match OUTSIDE_THE_CONTRACT.iter().find(|(exempt, _)| exempt == id) {
|
|
510
|
+
Some((_, reason)) => assert!(
|
|
511
|
+
!reason.trim().is_empty(),
|
|
512
|
+
"{id} is exempted from the contract with no reason"
|
|
513
|
+
),
|
|
514
|
+
None => missing.push(format!("{id} ({})", claim.status)),
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
missing.sort();
|
|
518
|
+
|
|
519
|
+
assert!(
|
|
520
|
+
missing.is_empty(),
|
|
521
|
+
"certified and absent from the contract: {}",
|
|
522
|
+
missing.join(", ")
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
for (id, _) in OUTSIDE_THE_CONTRACT {
|
|
526
|
+
assert!(
|
|
527
|
+
certified.contains_key(*id),
|
|
528
|
+
"{id} is exempted from a contract it was never in"
|
|
529
|
+
);
|
|
530
|
+
assert!(
|
|
531
|
+
!mentioned.contains(id),
|
|
532
|
+
"{id} is exempted and also named by a promise"
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/// **Every gap becomes somebody's job.**
|
|
538
|
+
///
|
|
539
|
+
/// A claim that is not `Proven` is work FeltDB is not doing. If nothing else
|
|
540
|
+
/// picks it up, the limitation is real and invisible at the same time — the
|
|
541
|
+
/// worst combination. So each one must produce at least one operator
|
|
542
|
+
/// obligation.
|
|
543
|
+
#[test]
|
|
544
|
+
fn every_withheld_claim_creates_an_operator_obligation() {
|
|
545
|
+
let certified = certification();
|
|
546
|
+
let excused: BTreeSet<&str> = OUTSIDE_THE_CONTRACT.iter().map(|(id, _)| *id).collect();
|
|
547
|
+
let owned: BTreeSet<&str> = OBLIGATIONS
|
|
548
|
+
.iter()
|
|
549
|
+
.flat_map(|obligation| obligation.because.iter().copied())
|
|
550
|
+
.collect();
|
|
551
|
+
|
|
552
|
+
let mut orphaned = Vec::new();
|
|
553
|
+
for (id, claim) in &certified {
|
|
554
|
+
let withheld = claim.status == "Unproven" || claim.status == "PartiallyProven";
|
|
555
|
+
if withheld && !excused.contains(id.as_str()) && !owned.contains(id.as_str()) {
|
|
556
|
+
orphaned.push(format!("{id} ({})", claim.status));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
orphaned.sort();
|
|
560
|
+
|
|
561
|
+
assert!(
|
|
562
|
+
orphaned.is_empty(),
|
|
563
|
+
"withheld and nobody's job: {}",
|
|
564
|
+
orphaned.join(", ")
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
for obligation in OBLIGATIONS {
|
|
568
|
+
assert!(
|
|
569
|
+
!obligation.because.is_empty(),
|
|
570
|
+
"{} is an obligation with no cause",
|
|
571
|
+
obligation.id
|
|
572
|
+
);
|
|
573
|
+
for id in obligation.because {
|
|
574
|
+
let claim = certified
|
|
575
|
+
.get(*id)
|
|
576
|
+
.unwrap_or_else(|| panic!("{} cites `{id}`, which is not a claim", obligation.id));
|
|
577
|
+
assert_ne!(
|
|
578
|
+
claim.status, "Proven",
|
|
579
|
+
"{} makes an operator responsible for `{id}`, which FeltDB proves",
|
|
580
|
+
obligation.id
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/// **A promise reads as a promise; a refusal reads as a refusal.**
|
|
587
|
+
///
|
|
588
|
+
/// Hedging inside a promise is how an unconditional guarantee gets quietly
|
|
589
|
+
/// weakened after the fact, and guarantee-language inside a withheld one is how
|
|
590
|
+
/// a gap gets quietly read as a feature. Both are wording failures that no
|
|
591
|
+
/// status check would catch.
|
|
592
|
+
///
|
|
593
|
+
/// The hedge rule applies to the promise itself and **not** to its condition.
|
|
594
|
+
/// Describing what a promise does not cover is exactly where words like "may"
|
|
595
|
+
/// belong: `Grouped`'s condition says up to `every - 1` acknowledged writes may
|
|
596
|
+
/// not yet have been through a barrier, and that sentence is precise, not
|
|
597
|
+
/// evasive. Forbidding it there would push the condition towards vaguer wording
|
|
598
|
+
/// to satisfy a check — the opposite of the intent.
|
|
599
|
+
#[test]
|
|
600
|
+
fn a_promise_does_not_hedge_and_a_refusal_does_not_guarantee() {
|
|
601
|
+
let hedges = [
|
|
602
|
+
" may ",
|
|
603
|
+
" might ",
|
|
604
|
+
" should ",
|
|
605
|
+
" typically",
|
|
606
|
+
" generally",
|
|
607
|
+
" usually",
|
|
608
|
+
"best effort",
|
|
609
|
+
"best-effort",
|
|
610
|
+
" aims to ",
|
|
611
|
+
" tries to ",
|
|
612
|
+
" intended to ",
|
|
613
|
+
];
|
|
614
|
+
let guarantees = [
|
|
615
|
+
"guarantees that",
|
|
616
|
+
"is guaranteed",
|
|
617
|
+
"is certified",
|
|
618
|
+
"we promise",
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
let mut problems = Vec::new();
|
|
622
|
+
for promise in CONTRACT {
|
|
623
|
+
let text = format!("{} {}", promise.promise, promise.condition).to_lowercase();
|
|
624
|
+
if promise.tier.promises() {
|
|
625
|
+
let promised = promise.promise.to_lowercase();
|
|
626
|
+
for hedge in hedges {
|
|
627
|
+
if promised.contains(hedge) {
|
|
628
|
+
problems.push(format!(
|
|
629
|
+
"{} is `{}` and hedges with \"{}\"",
|
|
630
|
+
promise.id,
|
|
631
|
+
promise.tier.label(),
|
|
632
|
+
hedge.trim()
|
|
633
|
+
));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
} else {
|
|
637
|
+
for guarantee in guarantees {
|
|
638
|
+
if common::asserts_phrase(&text, guarantee) {
|
|
639
|
+
problems.push(format!(
|
|
640
|
+
"{} is `Not certified` and asserts \"{guarantee}\"",
|
|
641
|
+
promise.id
|
|
642
|
+
));
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
assert!(problems.is_empty(), "{}", problems.join("\n"));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// ===========================================================================
|
|
652
|
+
// The published contract
|
|
653
|
+
// ===========================================================================
|
|
654
|
+
|
|
655
|
+
fn published_document() -> String {
|
|
656
|
+
let path = concat!(
|
|
657
|
+
env!("CARGO_MANIFEST_DIR"),
|
|
658
|
+
"/../../docs/architecture/production-contract.md"
|
|
659
|
+
);
|
|
660
|
+
std::fs::read_to_string(path).expect("the contract document exists and is readable")
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/// **The published contract is a rendering, not a retelling.**
|
|
664
|
+
///
|
|
665
|
+
/// Every promise appears with the same tier and the same backing. A promise
|
|
666
|
+
/// that exists only in the document, or a tier that differs between the two,
|
|
667
|
+
/// fails here — which is the case this whole file exists to prevent.
|
|
668
|
+
#[test]
|
|
669
|
+
fn the_published_contract_matches_the_executable_contract() {
|
|
670
|
+
let document = published_document();
|
|
671
|
+
let promised = contract();
|
|
672
|
+
|
|
673
|
+
let mut rows: BTreeMap<String, (String, String)> = BTreeMap::new();
|
|
674
|
+
for line in document.lines() {
|
|
675
|
+
let Some(cells) = common::table_cells(line) else {
|
|
676
|
+
continue;
|
|
677
|
+
};
|
|
678
|
+
// id | area | promise | tier | condition | backing
|
|
679
|
+
if cells.len() != 6 {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
let id = cells[0].clone();
|
|
683
|
+
let looks_like_an_id = id.len() >= 2
|
|
684
|
+
&& (id.starts_with('P') || id.starts_with('N'))
|
|
685
|
+
&& id[1..].chars().all(|c| c.is_ascii_digit());
|
|
686
|
+
if !looks_like_an_id {
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
assert!(
|
|
690
|
+
rows.insert(id.clone(), (cells[3].clone(), cells[5].clone()))
|
|
691
|
+
.is_none(),
|
|
692
|
+
"the document publishes `{id}` more than once"
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
let mut problems = Vec::new();
|
|
697
|
+
for promise in CONTRACT {
|
|
698
|
+
match rows.get(promise.id) {
|
|
699
|
+
None => problems.push(format!("{}: absent from the document", promise.id)),
|
|
700
|
+
Some((tier, backing)) => {
|
|
701
|
+
if tier != promise.tier.label() {
|
|
702
|
+
problems.push(format!(
|
|
703
|
+
"{}: the document publishes `{tier}`, the contract says `{}`",
|
|
704
|
+
promise.id,
|
|
705
|
+
promise.tier.label()
|
|
706
|
+
));
|
|
707
|
+
}
|
|
708
|
+
let expected = promise.backing.join(", ");
|
|
709
|
+
let published: String = backing.replace('`', "");
|
|
710
|
+
if published != expected {
|
|
711
|
+
problems.push(format!(
|
|
712
|
+
"{}: the document rests it on `{published}`, the contract rests it on `{expected}`",
|
|
713
|
+
promise.id
|
|
714
|
+
));
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
for id in rows.keys() {
|
|
720
|
+
if !promised.contains_key(id.as_str()) {
|
|
721
|
+
problems.push(format!("{id}: in the document, absent from the contract"));
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
assert!(problems.is_empty(), "{}", problems.join("\n"));
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/// Every tier is published, including an empty one, so a bucket cannot vanish
|
|
729
|
+
/// from the document because it was inconvenient.
|
|
730
|
+
#[test]
|
|
731
|
+
fn the_published_contract_publishes_every_tier() {
|
|
732
|
+
let document = published_document();
|
|
733
|
+
for tier in Tier::ALL {
|
|
734
|
+
assert!(
|
|
735
|
+
document.contains(tier.label()),
|
|
736
|
+
"the contract document never mentions `{}`",
|
|
737
|
+
tier.label()
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
for obligation in OBLIGATIONS {
|
|
741
|
+
assert!(
|
|
742
|
+
document.contains(obligation.id),
|
|
743
|
+
"{} is an operator obligation the document does not publish",
|
|
744
|
+
obligation.id
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/// The contract document is held to the same rule as every other document: it
|
|
750
|
+
/// may name a withheld guarantee, and may not assert one.
|
|
751
|
+
#[test]
|
|
752
|
+
fn the_contract_document_asserts_nothing_the_certification_withholds() {
|
|
753
|
+
let forbidden = [
|
|
754
|
+
"power-loss safe",
|
|
755
|
+
"power loss safe",
|
|
756
|
+
"guaranteed durable",
|
|
757
|
+
"compaction is non-blocking",
|
|
758
|
+
"compaction no longer blocks",
|
|
759
|
+
"all reads are bounded",
|
|
760
|
+
"supported capacity",
|
|
761
|
+
];
|
|
762
|
+
|
|
763
|
+
let prose = common::prose_excluding_withheld_rows(&published_document(), &["Not certified"]);
|
|
764
|
+
let mut violations = Vec::new();
|
|
765
|
+
for phrase in forbidden {
|
|
766
|
+
if common::asserts_phrase(&prose, phrase) {
|
|
767
|
+
violations.push(format!("the contract asserts \"{phrase}\""));
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
assert!(violations.is_empty(), "{}", violations.join("\n"));
|
|
771
|
+
}
|