Skip to content

Commit a30a997

Browse files
committed
docs(decisions): record the store's fault and recovery contract
ADR 0057 is amended in place, as ADR 0055 was, because the store's contract belongs in one document rather than two. The update records what rolls back and stays usable, what poisons, what recovery does and is allowed to fail at, and why the pair of termination cases is the property requirement 13 asks for rather than an extra. Both items its owed list carried are struck with the date they were delivered, and the section says plainly that the contract came out narrower than the first text implied.
1 parent dddc603 commit a30a997

2 files changed

Lines changed: 54 additions & 8 deletions

File tree

docs/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,13 @@ committed a rejected block passed the whole suite. Checking the stored
495495
transaction root also found the store's one duplicated derivation — it rebuilt a
496496
root the block header already commits to — and `execute_block` now carries that
497497
root out of the block, which changes no encoding, no state, and no accepted
498-
vector.
498+
vector. **It was amended in place on 2026-09-01** when the two things it recorded
499+
as owed were delivered, and the contract turned out narrower than its first text
500+
implied: everything before the commit rolls back and is an ordinary refusal that
501+
leaves the store usable, only the commit can leave a head the process cannot
502+
name, and there the store poisons itself and reads the file again — recovering to
503+
the block's root or its predecessor's and never to anything between, which is the
504+
property requirement 13 names when it says "through restart *and recovery*".
499505

500506
ADR 0058 records the version-seven application layer, which is the first piece
501507
that turns the store into something a consensus engine can drive. **Its whole

docs/decisions/0057-the-version-seven-owning-store.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ state meaning "the durable head is unknown" rather than "something went wrong".
9999

100100
If the write itself fails the store is **poisoned**: the durable head is whatever
101101
the transaction left and this process no longer knows which, so every later call
102-
returns `storage_failure`. Version one recovers by reopening; version seven does
103-
not yet, and that is recorded as owed rather than implied.
102+
returns `storage_failure`. **Recovery was owed at first publication and is
103+
recorded below as delivered on 2026-09-01.**
104104

105105
### The verifier is supplied at construction
106106

@@ -190,13 +190,53 @@ accepted the file. A second fuzz target over those would be exercising
190190
- The next slices are the application layer and the CometBFT adapter carrying
191191
version-seven transactions, which is what turns a store into a node.
192192

193+
## Update, 2026-09-01: the write path's faults and its recovery
194+
195+
The first two items of the owed list below are now delivered, and the contract
196+
they settle is narrower than the original text implied.
197+
198+
**Everything before the commit rolls back and is an ordinary refusal.** A fault
199+
at `before_transaction`, `after_transaction_begin`, `after_persistence`, or
200+
`before_commit` abandons the transaction, leaves the durable head the one it
201+
already was, and **leaves the store usable** — the same store accepts the same
202+
block once the fault is gone. The original text poisoned the store on any write
203+
failure, which was safe and wrong: a refusal that wrote nothing is not a reason
204+
to stop answering.
205+
206+
**Only the commit can leave a head this process cannot name**, and there the
207+
store poisons itself and then **reads the file again**. Recovery closes the
208+
connection, reopens it, runs the same four validation steps an ordinary open
209+
runs, and adopts whatever head the file actually holds — which is either the
210+
block's or its predecessor's, because SQLite's transaction is what decides and
211+
nothing between is reachable. On success the poison is cleared and the store
212+
continues.
213+
214+
**Recovery is allowed to fail, and then the store stays poisoned.** It is
215+
`noexcept` and answers `false`; a store that could not read its own file back
216+
refuses to read a head, refuses to hand out a payload, and refuses every later
217+
block. That is a worse state and an honest one.
218+
219+
The evidence is `version-seven-store-recovery`. The four rolled-back faults are
220+
each driven and then cleared, and the block that follows must reproduce its
221+
**recorded** root. A commit made to fail through the fault VFS's journal sync
222+
must recover to height zero, stay conserved, and then execute the same block to
223+
its recorded root. A commit whose recovery is *also* denied must refuse
224+
everything afterwards. And the process is **killed** at
225+
`after_commit_before_publication` and at `after_publication` by a re-executed
226+
child: in both cases the parent must find the committed block durable at its
227+
recorded root, and must be able to continue the chain to the next block's
228+
recorded root.
229+
230+
That last pair is the property requirement 13 names when it says "through
231+
restart **and recovery**": a fault anywhere in the write path leaves the durable
232+
head at the pre-block root or the post-block root, and never at anything between.
233+
193234
## Owed, and recorded rather than implied
194235

195-
- **Fault-injection coverage of the version-seven write path.** The seams exist
196-
and are reused; nothing yet drives them for this store, so the poisoned path is
197-
reasoned about rather than exercised.
198-
- **Recovery after a poisoned write.** Version one reopens and revalidates;
199-
version seven refuses every later call until the process restarts.
236+
- ~~**Fault-injection coverage of the version-seven write path.**~~ Delivered
237+
2026-09-01; see the update above.
238+
- ~~**Recovery after a poisoned write.**~~ Delivered 2026-09-01, with a narrower
239+
contract than this section first assumed; see the update above.
200240
- **The archive and block-history replay.** Version one validates its
201241
materialized head by replaying every block from genesis. Version seven's head
202242
is validated by the snapshot's gates and the conservation invariants instead,

0 commit comments

Comments
 (0)