@@ -99,8 +99,8 @@ state meaning "the durable head is unknown" rather than "something went wrong".
9999
100100If the write itself fails the store is ** poisoned** : the durable head is whatever
101101the 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