@optimystic/db-core 1.0.0-beta.3 → 1.1.0

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.
Files changed (171) hide show
  1. package/dist/src/cluster/structs.d.ts +30 -6
  2. package/dist/src/cluster/structs.d.ts.map +1 -1
  3. package/dist/src/cluster/structs.js +5 -3
  4. package/dist/src/cluster/structs.js.map +1 -1
  5. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +2 -1
  6. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -1
  7. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +10 -3
  8. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -1
  9. package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -1
  10. package/dist/src/cohort-topic/wire/codec.js +8 -2
  11. package/dist/src/cohort-topic/wire/codec.js.map +1 -1
  12. package/dist/src/collection/collection.d.ts +439 -34
  13. package/dist/src/collection/collection.d.ts.map +1 -1
  14. package/dist/src/collection/collection.js +843 -148
  15. package/dist/src/collection/collection.js.map +1 -1
  16. package/dist/src/collection/struct.d.ts +109 -4
  17. package/dist/src/collection/struct.d.ts.map +1 -1
  18. package/dist/src/collection/struct.js +88 -1
  19. package/dist/src/collection/struct.js.map +1 -1
  20. package/dist/src/collections/diary/diary.d.ts +6 -2
  21. package/dist/src/collections/diary/diary.d.ts.map +1 -1
  22. package/dist/src/collections/diary/diary.js +5 -2
  23. package/dist/src/collections/diary/diary.js.map +1 -1
  24. package/dist/src/collections/tree/struct.d.ts +124 -16
  25. package/dist/src/collections/tree/struct.d.ts.map +1 -1
  26. package/dist/src/collections/tree/struct.js +86 -11
  27. package/dist/src/collections/tree/struct.js.map +1 -1
  28. package/dist/src/collections/tree/tree.d.ts +13 -3
  29. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  30. package/dist/src/collections/tree/tree.js +106 -40
  31. package/dist/src/collections/tree/tree.js.map +1 -1
  32. package/dist/src/index.d.ts +1 -1
  33. package/dist/src/index.d.ts.map +1 -1
  34. package/dist/src/index.js +1 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/matchmaking/wire.d.ts.map +1 -1
  37. package/dist/src/matchmaking/wire.js +8 -2
  38. package/dist/src/matchmaking/wire.js.map +1 -1
  39. package/dist/src/network/durability.d.ts +45 -0
  40. package/dist/src/network/durability.d.ts.map +1 -0
  41. package/dist/src/network/durability.js +114 -0
  42. package/dist/src/network/durability.js.map +1 -0
  43. package/dist/src/network/i-key-network.d.ts +14 -6
  44. package/dist/src/network/i-key-network.d.ts.map +1 -1
  45. package/dist/src/network/index.d.ts +3 -0
  46. package/dist/src/network/index.d.ts.map +1 -1
  47. package/dist/src/network/index.js +3 -0
  48. package/dist/src/network/index.js.map +1 -1
  49. package/dist/src/network/lineage.d.ts +39 -0
  50. package/dist/src/network/lineage.d.ts.map +1 -0
  51. package/dist/src/network/lineage.js +61 -0
  52. package/dist/src/network/lineage.js.map +1 -0
  53. package/dist/src/network/routing-key.d.ts +24 -0
  54. package/dist/src/network/routing-key.d.ts.map +1 -0
  55. package/dist/src/network/routing-key.js +9 -0
  56. package/dist/src/network/routing-key.js.map +1 -0
  57. package/dist/src/network/stale-failure.d.ts +5 -1
  58. package/dist/src/network/stale-failure.d.ts.map +1 -1
  59. package/dist/src/network/stale-failure.js +5 -1
  60. package/dist/src/network/stale-failure.js.map +1 -1
  61. package/dist/src/network/struct.d.ts +151 -8
  62. package/dist/src/network/struct.d.ts.map +1 -1
  63. package/dist/src/network/struct.js.map +1 -1
  64. package/dist/src/reactivity/wire.d.ts.map +1 -1
  65. package/dist/src/reactivity/wire.js +8 -2
  66. package/dist/src/reactivity/wire.js.map +1 -1
  67. package/dist/src/testing/test-transactor.d.ts +64 -7
  68. package/dist/src/testing/test-transactor.d.ts.map +1 -1
  69. package/dist/src/testing/test-transactor.js +150 -23
  70. package/dist/src/testing/test-transactor.js.map +1 -1
  71. package/dist/src/transaction/coordinator.d.ts +141 -21
  72. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  73. package/dist/src/transaction/coordinator.js +408 -122
  74. package/dist/src/transaction/coordinator.js.map +1 -1
  75. package/dist/src/transaction/errors.d.ts +39 -10
  76. package/dist/src/transaction/errors.d.ts.map +1 -1
  77. package/dist/src/transaction/errors.js +34 -9
  78. package/dist/src/transaction/errors.js.map +1 -1
  79. package/dist/src/transaction/index.d.ts +1 -0
  80. package/dist/src/transaction/index.d.ts.map +1 -1
  81. package/dist/src/transaction/index.js.map +1 -1
  82. package/dist/src/transactor/block-floors.d.ts +78 -0
  83. package/dist/src/transactor/block-floors.d.ts.map +1 -0
  84. package/dist/src/transactor/block-floors.js +62 -0
  85. package/dist/src/transactor/block-floors.js.map +1 -0
  86. package/dist/src/transactor/change-notifier.d.ts +43 -0
  87. package/dist/src/transactor/change-notifier.d.ts.map +1 -1
  88. package/dist/src/transactor/change-notifier.js +3 -0
  89. package/dist/src/transactor/change-notifier.js.map +1 -1
  90. package/dist/src/transactor/index.d.ts +1 -0
  91. package/dist/src/transactor/index.d.ts.map +1 -1
  92. package/dist/src/transactor/index.js +1 -0
  93. package/dist/src/transactor/index.js.map +1 -1
  94. package/dist/src/transactor/network-transactor.d.ts +43 -6
  95. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  96. package/dist/src/transactor/network-transactor.js +279 -86
  97. package/dist/src/transactor/network-transactor.js.map +1 -1
  98. package/dist/src/transactor/transactor-source.d.ts +94 -4
  99. package/dist/src/transactor/transactor-source.d.ts.map +1 -1
  100. package/dist/src/transactor/transactor-source.js +120 -42
  101. package/dist/src/transactor/transactor-source.js.map +1 -1
  102. package/dist/src/transactor/transactor.d.ts +14 -1
  103. package/dist/src/transactor/transactor.d.ts.map +1 -1
  104. package/dist/src/transform/atomic.d.ts.map +1 -1
  105. package/dist/src/transform/atomic.js +12 -9
  106. package/dist/src/transform/atomic.js.map +1 -1
  107. package/dist/src/transform/base-pins.d.ts +50 -17
  108. package/dist/src/transform/base-pins.d.ts.map +1 -1
  109. package/dist/src/transform/base-pins.js +58 -11
  110. package/dist/src/transform/base-pins.js.map +1 -1
  111. package/dist/src/transform/cache-source.d.ts +75 -11
  112. package/dist/src/transform/cache-source.d.ts.map +1 -1
  113. package/dist/src/transform/cache-source.js +162 -36
  114. package/dist/src/transform/cache-source.js.map +1 -1
  115. package/dist/src/transform/digest.d.ts +8 -1
  116. package/dist/src/transform/digest.d.ts.map +1 -1
  117. package/dist/src/transform/digest.js +19 -9
  118. package/dist/src/transform/digest.js.map +1 -1
  119. package/dist/src/transform/helpers.d.ts +6 -2
  120. package/dist/src/transform/helpers.d.ts.map +1 -1
  121. package/dist/src/transform/helpers.js +8 -3
  122. package/dist/src/transform/helpers.js.map +1 -1
  123. package/dist/src/transform/tracker.d.ts +94 -14
  124. package/dist/src/transform/tracker.d.ts.map +1 -1
  125. package/dist/src/transform/tracker.js +222 -68
  126. package/dist/src/transform/tracker.js.map +1 -1
  127. package/dist/src/utility/structural-equals.d.ts +38 -0
  128. package/dist/src/utility/structural-equals.d.ts.map +1 -0
  129. package/dist/src/utility/structural-equals.js +77 -0
  130. package/dist/src/utility/structural-equals.js.map +1 -0
  131. package/package.json +2 -2
  132. package/src/cluster/structs.ts +28 -7
  133. package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +260 -253
  134. package/src/cohort-topic/wire/codec.ts +222 -216
  135. package/src/collection/collection.ts +960 -157
  136. package/src/collection/struct.ts +99 -4
  137. package/src/collections/diary/diary.ts +71 -68
  138. package/src/collections/tree/struct.ts +155 -23
  139. package/src/collections/tree/tree.ts +127 -40
  140. package/src/index.ts +1 -1
  141. package/src/matchmaking/wire.ts +8 -2
  142. package/src/network/durability.ts +123 -0
  143. package/src/network/i-key-network.ts +60 -52
  144. package/src/network/index.ts +10 -7
  145. package/src/network/lineage.ts +80 -0
  146. package/src/network/routing-key.ts +27 -0
  147. package/src/network/stale-failure.ts +5 -1
  148. package/src/network/struct.ts +156 -8
  149. package/src/reactivity/wire.ts +230 -224
  150. package/src/testing/test-transactor.ts +152 -24
  151. package/src/transaction/coordinator.ts +470 -131
  152. package/src/transaction/errors.ts +34 -9
  153. package/src/transaction/index.ts +1 -0
  154. package/src/transactor/block-floors.ts +105 -0
  155. package/src/transactor/change-notifier.ts +129 -80
  156. package/src/transactor/index.ts +1 -0
  157. package/src/transactor/network-transactor.ts +297 -89
  158. package/src/transactor/transactor-source.ts +131 -42
  159. package/src/transactor/transactor.ts +13 -1
  160. package/src/transform/atomic.ts +25 -22
  161. package/src/transform/base-pins.ts +76 -18
  162. package/src/transform/cache-source.ts +327 -194
  163. package/src/transform/digest.ts +21 -10
  164. package/src/transform/helpers.ts +8 -3
  165. package/src/transform/tracker.ts +423 -270
  166. package/src/utility/structural-equals.ts +72 -0
  167. package/dist/src/utility/block-id-to-bytes.d.ts +0 -3
  168. package/dist/src/utility/block-id-to-bytes.d.ts.map +0 -1
  169. package/dist/src/utility/block-id-to-bytes.js +0 -7
  170. package/dist/src/utility/block-id-to-bytes.js.map +0 -1
  171. package/src/utility/block-id-to-bytes.ts +0 -8
@@ -34,11 +34,14 @@ export type PendRequest = ActionTransforms & {
34
34
  * 'r' is return, which fails but returns the pending ActionIds and their transforms */
35
35
  policy: 'c' | 'f' | 'r';
36
36
  /**
37
- * Present only on the multi-collection path (`TransactionCoordinator.pendCollection`): the
38
- * transaction to re-execute plus the hash of ALL operations across all blocks it must produce.
39
- * Absent on the single-collection `Collection.sync` path, which carries bare transforms and is
40
- * therefore not re-checkable — see `ClusterConsensusConfig.unvalidatablePendPolicy` for what a
41
- * validating receiver does with that shape. ONE optional pair, deliberately: "transaction
37
+ * Present only on a validated multi-collection pend (`TransactionCoordinator.pendCollection`
38
+ * under its default `pendValidation: 'transaction'`): the transaction to re-execute plus the
39
+ * hash of ALL operations across all blocks it must produce. Absent on every pend that carries
40
+ * bare transforms and is therefore not re-checkable — the single-collection `Collection.sync`
41
+ * path, and a coordinator built with `pendValidation: 'none'` (the Quereus adapter's legacy
42
+ * multi-tree commit, which has no statements to re-execute) — see
43
+ * `ClusterConsensusConfig.unvalidatablePendPolicy` for what a validating receiver does with
44
+ * that shape. ONE optional pair, deliberately: "transaction
42
45
  * without its hash" (or the reverse) was a state the old two independent optional fields
43
46
  * permitted and no producer ever created — and a receiver whose guard required both could be
44
47
  * talked out of validating by a sender that omitted one.
@@ -52,27 +55,103 @@ export type PendRequest = ActionTransforms & {
52
55
  /** For multi-collection transactions: supercluster nominees for consensus */
53
56
  superclusterNominees?: PeerId[];
54
57
  /**
55
- * Aged, advisory retry priority for the *single-collection* pend path (default 0 when absent).
56
- * The multi-collection path instead carries priority on the transaction inside
58
+ * Aged, advisory retry priority for a pend that carries no transaction (default 0 when absent).
59
+ * A validated multi-collection pend instead carries priority on the transaction inside
57
60
  * {@link PendRequest.validation} ({@link Transaction.priority}); this top-level field is the
58
- * carrier for a `Collection.sync` pend, which has no transaction. A cluster member reads whichever is present as the first
61
+ * carrier for every pend without one — a `Collection.sync` pend, and an unvalidated
62
+ * coordinator pend (`pendValidation: 'none'`). A cluster member reads whichever is present as the first
59
63
  * `resolveRace` tiebreak. FAIRNESS-ONLY: it rides inside the signed cluster `message` (so it is
60
64
  * integrity-protected in transit) but MUST NOT affect the operations hash, stale-read checks, or
61
65
  * validity — a stale pend is still rejected regardless of priority.
62
66
  */
63
67
  priority?: number;
68
+ /**
69
+ * Per block, the committed revision of the base the author's update operations were computed
70
+ * against (`Tracker.stagedBaseRevs`). Present for a block whose transform is update-only and
71
+ * whose base the author knows; never for an inserted or deleted block (base-independent), and
72
+ * absent for a block the author did not read through a source that reports revisions. Omitted
73
+ * entirely when empty (`baseRevsField`): the request is hashed verbatim into every cohort
74
+ * signature preimage, so a pend that declares nothing must serialize exactly as it did before
75
+ * the field existed. Action-wide here; the transactor narrows it to each per-coordinator batch's
76
+ * own block ids at send time, exactly as it narrows {@link CommitRequest.blockDigests}.
77
+ *
78
+ * Storage keeps it beside the pending record (`BlockMetadata.pendingBases` in db-p2p), so every
79
+ * later step that applies the record can refuse to apply it to a different version of the block
80
+ * — including the read-driven promotion in `StorageRepo.get`, which has no commit message to
81
+ * consult. The commit's own `blockDigests[id].baseRev` is now the fallback for a record whose
82
+ * pend named no base, not the primary check.
83
+ */
84
+ baseRevs?: BlockBaseRevs;
64
85
  };
65
86
 
66
87
  export type BlockActionStatus = ActionBlocks & {
67
88
  statuses: ('pending' | 'committed' | 'checkpointed' | 'aborted' | 'committed-invalidated')[];
68
89
  };
69
90
 
91
+ /** How widely a successful write is known to be held at the moment it was acknowledged. */
92
+ export type DurabilityQuorum =
93
+ /** Every member of the resolved cohort confirmed it holds the revision. */
94
+ | 'full'
95
+ /** A strict majority confirmed — the acknowledgement bar — but at least one member did not.
96
+ * Also the class of a torn action whose cohort confirmed everything it was asked for
97
+ * (`withTornBlocks` clamps `full` down to this; `torn` then names the blocks, and `unconfirmed`
98
+ * may be empty). */
99
+ | 'majority'
100
+ /** The cohort resolved, and it is this node alone. Nobody else holds the revision yet. */
101
+ | 'local'
102
+ /** The cohort did NOT resolve: the lookup threw, or named nobody, or named a single peer that
103
+ * is not this node. The write is on this node's storage and the network was never consulted.
104
+ * Deliberately NOT `local`: a genuine cohort of one is a correct, complete write, whereas this
105
+ * is a write whose correct destination is unknown. */
106
+ | 'unrouted';
107
+
108
+ /** What the answering layer knows about who holds a successful write. Each layer refines it:
109
+ * `StorageRepo` answers for itself alone, `CoordinatorRepo` answers for the cohort the write ran
110
+ * on, and `NetworkTransactor` merges the per-coordinator answers into one action-level answer.
111
+ * Never a policy input — read it to DISPLAY or to REPAIR, never to decide acceptance.
112
+ *
113
+ * The only "this write is completely saved" test is `isFullyDurable` (`network/durability.ts`);
114
+ * do not compare `quorum` by hand, because a `full` cohort report can still sit on an action that
115
+ * abandoned blocks (`torn`). */
116
+ export type WriteDurability = {
117
+ readonly quorum: DurabilityQuorum;
118
+ /** Cohort members confirmed to hold the write, including this node when it holds it.
119
+ * For a commit this means "reported that its storage durably holds the revision"; for a pend
120
+ * it means "accepted the pending record". The two are NOT comparable — a pend confers no
121
+ * storage durability (see the note in `CoordinatorRepo.pendThroughCluster`). */
122
+ readonly confirmed: number;
123
+ /** Size of the cohort the write ran on. Zero exactly when `quorum` is 'unrouted'. */
124
+ readonly cohort: number;
125
+ /** Cohort members that did not confirm, by peer-id string. Empty when `quorum` is 'full'.
126
+ * ABSENT (not empty) when the answering layer could not name the cohort — an empty array
127
+ * here means "named the cohort, nobody is missing". */
128
+ readonly unconfirmed?: readonly string[];
129
+ /** The cohort the write ran on, by peer-id string. Absent where the answering layer has no
130
+ * cohort view at all (a bare `StorageRepo` verdict). */
131
+ readonly cohortPeerIds?: readonly string[];
132
+ /** Present only on an action-level result assembled from MORE THAN ONE distinct coordinator
133
+ * answer: the other answers. Identical answers are collapsed (`mergeDurability`), so a healthy
134
+ * single-cohort commit has none; one cohort answering differently for two batches appears
135
+ * twice. The scalar fields above always describe the WEAKEST answer, so a consumer that reads
136
+ * only them is reading the binding constraint and is never over-optimistic. */
137
+ readonly otherCohorts?: readonly WriteDurability[];
138
+ /** Blocks this action wrote that are NOT committed — a torn sweep abandoned and cancelled them
139
+ * (`NetworkTransactor.cancelAbandonedSweepBlocks`). Action-level commit results only.
140
+ * These blocks NEVER heal by replication: their transform is gone, and only the writer
141
+ * re-driving the action puts them back. */
142
+ readonly torn?: readonly BlockId[];
143
+ };
144
+
70
145
  export type PendSuccess = {
71
146
  success: true;
72
147
  /** List of already pending actions that were found on blocks touched by this pend */
73
148
  pending: ActionPending[];
74
149
  /** The affected blocks */
75
150
  blockIds: BlockId[];
151
+ /** Who accepted the pending record. REQUIRED, not optional: an absent field would read as plain
152
+ * success, which is exactly the ambiguity this field exists to remove (a solo write, a write
153
+ * whose cohort never resolved, and a quorum write all used to answer `{ success: true }`). */
154
+ durability: WriteDurability;
76
155
  };
77
156
 
78
157
  export type StaleFailure = {
@@ -124,6 +203,12 @@ export type BlockContentDigest = {
124
203
  * digest without a network read is simply omitted, and falls back to corroboration downstream. */
125
204
  export type BlockContentDigests = Record<BlockId, BlockContentDigest>;
126
205
 
206
+ /** Per block, the committed revision its update operations were computed against — the shape
207
+ * `Tracker.stagedBaseRevs` produces and {@link PendRequest.baseRevs} carries. Optional per id: an
208
+ * inserted or deleted block is base-independent and never named; a block updated without a known
209
+ * base is simply omitted. */
210
+ export type BlockBaseRevs = Record<BlockId, number>;
211
+
127
212
  export type CommitRequest = ActionBlocks & {
128
213
  /** The header block of the collection, present only when this commit creates the collection
129
214
  * (the header is a fresh insert). Collection-identifying metadata, not an ordering signal —
@@ -185,6 +270,11 @@ export type CommitSuccess = {
185
270
  success: true;
186
271
  /** If present, the identified collection acts as the coordinator for the multi-collection transaction */
187
272
  coordinatorId?: CollectionId;
273
+ /** Who holds the committed revision. REQUIRED for the same reason as {@link PendSuccess.durability}:
274
+ * a quorum commit, a solo commit, a commit whose cohort lookup failed, and a torn multi-block
275
+ * commit all answered `{ success: true }` before this field existed, and a writer could not tell
276
+ * them apart (GitHub #19). Test it through `isFullyDurable`, never by comparing `quorum`. */
277
+ durability: WriteDurability;
188
278
  };
189
279
 
190
280
  export type BlockActionState = {
@@ -197,6 +287,61 @@ export type BlockActionState = {
197
287
  export type BlockGets = {
198
288
  blockIds: BlockId[];
199
289
  context?: ActionContext; // Latest if this is omitted
290
+ /** Per block, the lowest revision the ASKER can accept for it — its floor. Omit a block that
291
+ * has none; omit the field entirely when no block does.
292
+ *
293
+ * A floor is knowledge only the asker holds: a collection that walked the log entry
294
+ * "action A, committed at revision r, changed block X" knows a read of X at a context at or
295
+ * above `r` must come back materialized at `r` or later, while the repo answering may honestly
296
+ * believe its older copy is current. `TransactorSource.tryGet` fills this from the collection's
297
+ * `BlockFloors`; `NetworkTransactor.get` treats an answer under a block's floor as NOT answered,
298
+ * so it earns the same second-chance round against a different coordinator that `unavailable`
299
+ * and `unconfirmedAheadRev` earn.
300
+ *
301
+ * CLIENT-SIDE HINT, never on the wire: `NetworkTransactor.get` builds its own
302
+ * `{ blockIds, context }` for each downstream `IRepo.get`, so this field reaches no peer and no
303
+ * coordinator's freshness decision. An `ITransactor` that ignores it (`TestTransactor`, the
304
+ * reference peer's) stays correct, because the reader-side check that actually enforces the
305
+ * floor runs on the merged answer either way (`TransactorSource` / `BlockFloorCheck`).
306
+ *
307
+ * NOTE: the retry `NetworkTransactor.get` runs is per BATCH, not per block, so one below-floor
308
+ * block re-asks every block that shared its coordinator. Costs nothing today — `tryGet` asks
309
+ * for one block per request, so this map never holds more than one entry — but a read source
310
+ * that batches would pay it. Split the retry payload down to the failing block ids then. */
311
+ floors?: Record<BlockId, number>;
312
+ /** Ask the answering repo whether each block's CURRENT content was built from this committed
313
+ * action at this revision; it answers per block in {@link GetBlockResult.lineage}. On the wire,
314
+ * unlike `floors`. A repo that predates the field leaves `lineage` absent, which every consumer
315
+ * reads as `unknown`. Asked only by a writer deciding whether its own superseded write is saved
316
+ * (`ITransactor.getLineage`); an ordinary read never sets it. */
317
+ lineageOf?: ActionRev;
318
+ };
319
+
320
+ /**
321
+ * Whether a block's current content was BUILT FROM a given committed `(actionId, rev)` — the
322
+ * write's transform is part of what the block holds now, even though later actions have since
323
+ * taken later revisions.
324
+ *
325
+ * - `contains` — it was. Holding that exact revision as the latest counts.
326
+ * - `excludes` — it provably was not: another action holds that revision of the block, or the
327
+ * block's history spans that revision without it. Nothing can land the write there any more.
328
+ * - `behind` — the block has not reached that revision (or does not exist). The write is not
329
+ * there, and could only get there through one of its own pending records.
330
+ * - `unknown` — not established either way.
331
+ *
332
+ * One repo answers from its own records only ({@link GetBlockResult.lineage}); a transactor
333
+ * answers for the block's cohort ({@link ActionLineage}).
334
+ */
335
+ export type BlockLineage = 'contains' | 'excludes' | 'behind' | 'unknown';
336
+
337
+ /** A cohort-level answer to "is this committed write part of what these blocks hold now?" — see
338
+ * `ITransactor.getLineage`. */
339
+ export type ActionLineage = {
340
+ /** One answer per requested block id, in request order. */
341
+ blocks: BlockLineage[];
342
+ /** Who holds the write, present exactly when EVERY block answered `contains`: the weakest
343
+ * block's cohort report, by the same rule a commit's durability is merged. */
344
+ durability?: WriteDurability;
200
345
  };
201
346
 
202
347
  /** Why a repo could not establish whether a block exists. Present ONLY when the repo
@@ -262,6 +407,9 @@ export type GetBlockResult = {
262
407
  * the content here is real, it may just be behind. Absent = confirmed, so every
263
408
  * producer that omits it keeps its meaning. */
264
409
  unconfirmedAheadRev?: number;
410
+ /** This repo's answer to {@link BlockGets.lineageOf}, from its OWN records only — no cohort
411
+ * view. Absent when the question was not asked, or the repo predates it. */
412
+ lineage?: BlockLineage;
265
413
  };
266
414
 
267
415
  /**