minerva 0.2.0

Causal ordering for distributed systems
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
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
= Minerva for Downstream Consumers
:toc: macro
:toclevels: 3

_Supported surfaces and caller-gated capabilities._

This guide is the consumer companion to xref:../DOCS.adoc[`DOCS.adoc`]. It
identifies stable, changing, and deferred capabilities. It also states the
caller evidence that can activate a deferred capability. The recorded
consumers are `plethos` and `pinax`, `ethos`, `stokes`, `alma`, and `polis`.

This document is a guide, not a contract. The capability PRDs, module notes,
code, and tests are authoritative. If this guide disagrees with those sources,
this guide is stale.

toc::[]

== How to read this

Minerva `0.1.0` contained only `kairos`. Version `0.2.0` adds the `metis`
decision layer, `Kairos::to_bytes`, `Clock::observe`, the gate seam, the
`Dot` identity type, and the recorded-membership implementation.

The version 2 membership frames are dormant siblings of the frozen version 1
family. A machine without admissions emits version 1. A version 1 reader
refuses a complete version 2 frame. Only a joint registry decision activates
the version 2 family. A release does not activate it.

Use `minerva = "0.2"` for normal SemVer resolution. Use
`minerva = "=0.2.0"` when you require one exact artifact. Before the owner
completes a publication, use a path dependency or an immutable Git revision.

No external consumer requires backward compatibility by default. These rules
apply:

* *Additive* changes can add a method, type, or default-preserving generic
  parameter. Consumers adopt them when needed.
* *Breaking* changes are permitted before external adoption. A breaking change
  must be deliberate, consistent across its boundary, and recorded with a
  migration note.
* *Deferred* capabilities have an accepted design but no implementation. A
  caller request must identify the application boundary that activates one.

Status legend, used throughout:

[horizontal]
*Stable*:: Shipped and tested. Build on it.
*Deferred*:: Designed, not built. Caller-gated. Request to pull.
*Hazard*:: A sharp edge in the stable surface to design around now.

== The stable surface

The following sections summarize the supported layers. Signatures are
abbreviated. See the module notes and rustdoc for complete signatures.

=== `kairos` (the stamp and the clock)

[horizontal]
`Kairos`:: A 128-bit causal stamp, `(physical, logical, kairotic, station_id)`, whose derived
  `Ord` _is_ the causal-then-spatial total order. `to_bytes` / `from_bytes` give a fixed
  17-byte big-endian frame that sorts causally; `KAIROS_WIRE_LEN` is public so you can split an
  envelope. The `kairotic` tag is yours: a caller-defined qualitative dimension ranked above
  `station_id`. A constant tag (`kairotic = 0`) neutralizes it if you do not want it
  (`pinax` / `plethos` ADR-0009 takes this opt-out). A varying tag is a caller protocol,
  not merely a number: define its admitted rank space, equal-tag meaning, neutral opt-out,
  and unknown-value refusal. Stokes' first implemented admission-side scheme maps nonzero
  command classes `1..=4` in precedence order, rejects unknown tags, and validates each
  payload class against its stamp before admission; its production emitter is not built yet.
  See xref:glossary.adoc[the glossary's kairotic-scheme entry].
`Clock<TS: TimeSource>`:: Lock-free HLC clock over one `AtomicU128`, owning
  its source by concrete type and allocating nothing for that source.
  `DynClock` is the explicit `Box<dyn TimeSource>` form; `LocalClock<TS>` and
  `DynLocalClock` are the single-threaded twins. `now(kairotic)` mints (send), `after(prev)`
  fuses receive-then-send, `observe(remote)` is the pure receive rule (advance past a remote
  stamp without minting), and `try_observe(remote, max_forward_skew)` is its bounded,
  trust-aware form (reject a too-far-future remote; see below). Generation is infallible; only
  `Clock::new`, `Clock::with_default_config`, `LocalClock::new`, and
  `LocalClock::with_warning_threshold` return `Result<_, InvalidStationId>`
  when `station_id` is zero. `Clock` is `Send + Sync` and can be shared as
  `Arc<Clock<TS>>`.
`TimeSource` / `Backoff`:: The injected effect seams. `Backoff` is time-free (a bounded spin
  count, not a measured delay).

=== `metis` (the decision layer)

[horizontal]
`VersionVector`:: The lattice that recovers what a scalar stamp cannot: _ordered
  versus concurrent_. `get` / `increment` / `observe` / `merge` (the join) / `meet` (the greatest
  lower bound, S78) / `iter`, plus
  `PartialOrd` (never `Ord`, the order is genuinely partial). `restrict(roster)` (S89) reads
  the vector relative to a sub-roster, and `try_glue(&other, overlap)` is the exact merge of
  partial views, refusing overlap disagreement with a typed `Disagreement` (see the relative
  reads section below). `difference(&other)` (S95) is the owed set, the least vector whose
  merge into the other side covers this one (see the difference reads section below).
  Canonical wire frame through
  `encoded_len` / `to_bytes` / `from_bytes` / `from_prefix`, with exact
  allocation-free length preflight and decode validating canonical form and
  bounding allocation on untrusted input, with a `DecodeError`.
`Event<T, D = VersionVector>`:: A stamped event `(stamp, deps, payload)`. `D` is the gate's
  dependency descriptor; the default is a `VersionVector` (causal), so `Event<T>` still names
  the causal event.
`Gate` (`Causal`, `Fifo`):: The buffer's stability rule, the seam the delivery machine runs
  over. `Causal` is the happens-before `VersionVector` rule; `Fifo` is the scalar per-source
  watermark (`Dep = u64`). They are *peers*; neither is a silent default.
`Released<T, G>`:: An affine, gate-branded witness minted only after `Ideal<T, G>` removes a ready
  pending occurrence and advances `G`. Borrow its event data for audit or downstream policy;
  consume it with `into_event` or `into_payload` only when deliberately erasing provenance. It is
  neither authority nor durable evidence.
`Ideal<T, G: Gate>` (`CausalIdeal<T>`, `FifoIdeal<T>`):: The delivery buffer. `insert` an
  event, drain with `pop_ready` until `None`; release respects causality and linearizes the
  concurrent frontier by `Kairos`. `pop_ready_event` is the same release handing back a
  `Released<T, G>` over the whole `Event` (`stamp` and `deps`), not just the payload, so you can tell a causally-forced release
  (`a -> b`) from a `Kairos` tiebreak between concurrent events; `pop_ready` is its payload
  projection. `frontier()` exposes that frontier read-only (the ready antichain under `Causal` /
  `Fifo`). `pop_ready_event_where` filters the live ready set and atomically releases its
  `Kairos`-minimal eligible member; a refusal advances nothing. `pending_len()` for backlog
  visibility; `delivered()` (causal) / `progress()` (any gate) for per-source progress.
`Producer<C = DynClock>`:: The send half. `C` is a sealed `ClockCarrier`:
  an owned `Clock<TS>`, `&Clock<TS>`, or `Arc<Clock<TS>>`.
  `produce(kairotic, payload)` mints a causal `Event`;
  `observe(&event)` learns from a received one (receipt, not application delivery: the buffer's
  `pop_ready` is delivery); `try_observe(&event, max_forward_skew)` is the bounded, all-or-nothing
  receipt (the `Clock::try_observe` skew bound lifted over the knowledge merge). Share the
  carrier when the station has other stamping sites (see the shared-clock
  section below). Composes with an `Ideal` rather than owning one, so you wire the two: feed each
  arrival to your producer (to observe) and your buffer (to reorder).
`Stability`:: The causal-stability tracker (S78): a fixed, caller-declared roster of stations,
  `report(station, &delivered)` join-folding each member's delivered cut (non-roster reporters
  refused with `UnknownStation`), and `watermark()` the n-ary `meet`: the greatest cut every
  member has passed, hence safe to forget. Mechanism only; what you reclaim below it, and when
  rosters change, is yours (see the causal-stability section below).
`DotSet`:: The exact have-set (S83, PRD 0012): records per station exactly which dots (1-based
  event indexes; a 0-based sequence maps `dot = sequence + 1`) have been received, however
  disordered the arrival. `insert` / `contains` / `merge` (the union join) / `from_witnessed` /
  `restrict` (S89, the sub-roster read, exact against every fold here), and the
  two cuts that bracket disordered receipt: `floor()`, the greatest gap-free prefix, a genuine
  cut by construction and the honest `Stability` report; `high_water()`, the per-station
  maximum, a liveness bound only (over holes it is an upper bound of a cut, never a stability
  report). `holes()` names the missing dots (the repair fetch list), with `holes_for(station)`
  its per-station view and `hole_count()` the repair-debt norm (both S98, PRD 0016);
  `difference(&other)` (S95) enumerates the exact dots you hold that a peer lacks (the serve
  list, the other direction of the same read), `intersect(&other)` (S96) is the meet (what both
  hold), `exceptions_len()` is the reorder-exposure count, and `exceptions()` walks those sparse
  dots in canonical order. `station_count`, `station`, and `stations` expose
  allocation-free `DotSetStation` row summaries carrying station, floor, high water, and forward
  gap. To ship a have-set across a
  boundary, `to_bytes` / `from_bytes` / `from_prefix` is the canonical run-length frame (S98,
  PRD 0016; a v1 byte-identity contract like the vector frame, decode validating canonical form
  and bounding allocation on untrusted input with a `HaveSetDecodeError`). An embedding
  protocol with a validated exception ceiling uses `HaveSetDecodeBudget` with
  `from_bytes_with_budget` or `from_prefix_with_budget` (S242); the unqualified
  decoders retain their input-length secure default; `encoded_len()` preflights the exact
  canonical size without allocating. Polis S2 is the first
  disordered-receipt adopter: its fact possession, bounded serve list, and paged holes use this
  surface directly, and Polis S3 (`5d10141`) embeds v1 in its authenticated
  possession protocol. That downstream protocol pins byte identity under R-8;
  changing the inner frame requires a coordinated Polis protocol version. As of S96 `DotSet`
  is also the causal *context* type and the simplest causal store (see the causal store section
  below).
`Dotted<S: DotStore>` / `DotMap<K, S>`:: The causal store algebra (S96, PRD 0015): a store
  of dot-tagged content bracketed by the context of everything ever seen, whose `merge`
  (the only write) honors removals without tombstones and keeps unseen concurrent writes.
  `DotStore` is an open axis like `Gate`: Minerva ships `DotSet`, `DotFun<V>`,
  `DotMap<K, S>`, and its structured stores; implement it for a different shape. See the
  causal store section below.

== Composing gates: the algebra

*Status: Stable (S237). The shipped leaf gates are `Causal` and `Fifo`; `And<A, B>` is the
shipped product over `ReleaseDrivenGate` components. `MapDep` remains caller-side until a
carried-dependency adapter has a renter.*

The gate axis has structure, not just a catalog. It is closed under two combinators and provably
not under a third, recorded in xref:metis-gate-algebra.adoc[the gate-algebra note]:

[horizontal]
`And<A, B>` (product):: Deliver only when *both* rules say stable. `Dep = (A::Dep, B::Dep)`,
  `deliverable` is conjunction, `advance` is componentwise, `stale` is *either*. Sound and
  contract-preserving when both components implement `ReleaseDrivenGate`: release must be their
  only progress authority. This is how you require "causally complete *and* endorsed" in one rule
  (`And<Causal, Participation>`). Progress is `AndProgress<A::Progress, B::Progress>`, whose
  `PartialOrd` is componentwise; use `left()` / `right()` to read components and `into_parts()`
  to consume them. `EpochGate` is deliberately not composable: `adopt_epoch` is separate checked
  authority, for which a product-aware API does not exist.
`MapDep<G, P>` (reindex):: Reuse a gate `G` over a *different carried dependency* through a pure
  projection `P: Wire -> G::Dep`. Adapts the dependency only, never the progress.
`Or<A, B>`:: A deliberate *non-goal*, twice over. A general `Or` breaks stable up-closure (let one
  component go stale while the other still waits, and the composite falls back to *waiting*,
  stranding the event), and even one that did not would break the implicit dynamic contract: on a
  disjunctive release only one component fired, so advancing both corrupts a stateful progress and
  advancing one fractures it. Do not build it.

The load-bearing rules when you author your own gate. First, the static laws, terminal `stale`,
disjoint from `deliverable`, *stable up-closure* (`deliverable || stale` is itself up-closed, which
does **not** follow from the other two), and monotone `advance`, are necessary. Check them with
`check_gate_laws`, which tests all four.

Second, they are not sufficient. `advance` must fold only a *released* event, so `progress` stays
the faithful record of what your gate released. A monotone fold (a contributor set, a delivered
count, a watermark) satisfies this; a rule that advances progress for an event it did not gate does
not.

Run caller-generated comparable progress cases through
`check_gate_laws::<YourGate>(&lower, &upper, sender, &dep)`. The dependency-free checker reports
descending or incomparable inputs separately from monotone-advance, disjointness,
terminal-stale, and stable-up-closure violations, so it fits proptest, quickcheck, fuzz corpora, or
deterministic tables without owning a generator. It cannot prove that a combinator advances only a
component that admitted the release; keep that dynamic proof at the combinator boundary.

Implement `ReleaseDrivenGate` only when those releases are the gate's complete progress
authority. A gate opened by an external witness can still implement `Gate`, but must not opt into
product composition without a checked product-aware transition.

That rule has a consumption-side twin, for any threshold-shaped gate (one whose `deliverable`
fires when a tally crosses a bound): the machine releases a gated event at the *moment its
threshold crosses*, so a `Progress`-derived tally read at release time is the minimal crossing
prefix: every release looks threshold-weight, and a ranking built on release-time tallies
silently degenerates to your tiebreak. If you rank releases by accumulated weight, drain the
`Ideal` fully first (`pop_ready` to `None`, delivering the trailing contributions), then read;
fully drained, the tally is again a deterministic function of the event multiset. This is not a
buffer defect but the release-at-crossing semantics any holdback machine owes; `ethos` hit it in
testing (see its recipe below).

== Bounded delivery: `Ideal::try_insert` and causal-safe replacement

*Status: Stable (S30; S235 corrected by S236). S236 narrows replacement to
`Ideal<T, Causal>` and removes `BoundedInsert`'s dependency type parameter.*

`Ideal::insert` buffers unconditionally. An event whose dependencies never arrive (a lost
message, a crashed or silent peer) waits forever, so `pending` can grow without bound. That is
inherent to causal delivery over a lossy channel, and until now the only mitigation was to watch
`pending_len()` yourself. This slice gives the buffer the *mechanism* to cap its own backlog,
while leaving the *policy* with you.

Current surface:

[source,rust]
----
impl<T, G: Gate> Ideal<T, G> {
    /// Buffer an event, refusing to grow the live backlog past `capacity`.
    /// `Ok(())`     -> buffered, or dropped as a stale duplicate (counts against nothing).
    /// `Err(AtCapacity { event, capacity })` -> the live backlog was full; buffer unchanged.
    pub fn try_insert(
        &mut self,
        event: Event<T, G::Dep>,
        capacity: NonZeroUsize,
    ) -> Result<(), AtCapacity<T, G::Dep>>;
}

impl<T> Ideal<T, Causal> {
    /// Replace the first caller-approved causally maximal resident in `Kairos` order.
    pub fn try_insert_evicting_where<F>(
        &mut self,
        event: Event<T>,
        capacity: NonZeroUsize,
        evictable: F,
    ) -> Result<BoundedInsert<T>, AtCapacity<T>>
    where
        F: FnMut(&Event<T>) -> bool;
}

pub struct AtCapacity<T, D = VersionVector> {
    pub event: Event<T, D>,   // handed back so you can re-route without rebuilding it
    pub capacity: NonZeroUsize,
}

pub enum BoundedInsert<T> {
    Buffered,
    DroppedStale,
    Evicted { evicted: Event<T> },
}
----

How to adopt:

[source,rust]
----
let cap = NonZeroUsize::new(4096).expect("non-zero backlog cap");

match buf.try_insert(event, cap) {
    Ok(()) => {}                              // buffered (or dropped as a stale replay)
    Err(AtCapacity { event, .. }) => {
        // your give-up policy: retry later, shed, escalate, or evict-and-reinsert
        on_overflow(event);
    }
}
while let Some(payload) = buf.pop_ready() { deliver(payload); }
----

Three things to understand:

. *The bound is a property of the call, not of the buffer.* There is no `with_capacity`
  constructor and no stored capacity. `insert` (infallible, unbounded) and `try_insert`
  (bounded, fallible) are two ways to insert into the same buffer; you choose per call. This
  mirrors the `observe` / `try_observe` escape-hatch pattern in the error model: the infallible default
  is untouched, and an additive `try_*` surfaces a limit you may care about. Pass the same
  `capacity` consistently; a varying cap is a caller bug the type cannot catch.
. *The cap counts _live_ backlog.* When full, `try_insert` first reclaims provably-dead
  entries (a pre-delivery duplicate whose twin was already released, which `insert` otherwise
  lets linger forever) before rejecting, so the bound is not spent on garbage. That reclamation
  scan is `O(pending)` and is paid only on the full path, never the common one.
. *We own causal safety; you own value policy.* `try_insert_evicting_where` exists only on
  `Ideal<T, Causal>`. On the full path Minerva filters out the arrival's predecessors and
  residents that any other pending event depends on. Your predicate sees only the remaining
  causally maximal candidates and may rank them by class, deadline, age, or application value.
  Refusal, retry, quarantine, and shedding remain caller policy.

Use replacement only when the admission policy is explicit:

[source,rust]
----
match buf.try_insert_evicting_where(event, cap, is_lower_priority) {
    Ok(BoundedInsert::Buffered | BoundedInsert::DroppedStale) => {}
    Ok(BoundedInsert::Evicted { evicted }) => quarantine(evicted),
    Err(AtCapacity { event, .. }) => on_overflow(event),
}
----

The operation is one-for-one and atomic: the backlog never grows, no eviction advances gate
progress, and the retained set acquires no missing predecessor. This is a safety guarantee, not
unconditional liveness: an already-missing predecessor or permanently false local eligibility
can still prevent progress. The returned victim remains a local command or message to retry,
reroute, quarantine, or audit. It has not been retracted from any replica.

What does _not_ change: `insert`, `new`, `Default`, `pop_ready`, `frontier`, `pending_len`,
release order, and every ordering invariant. If you do nothing, nothing changes.

This is the mechanism half of the long-standing "pending bound / give-up policy" item. The
policy half stays on the deferred menu below.

== Trust-aware receive: `Clock::try_observe`

*Status: Stable (additive, S24). No migration required.*

`Clock::observe` is infallible and unconditionally dominating: it advances `physical` to pass
any remote stamp. That is correct for honest peers (the skew is recorded in `stats()`, never
silently corrected), but once a transport admits *untrusted* stamps it is an attack surface, a
far-future `remote.physical` drags your clock forward without bound (hazard 1 below). This slice
gives the clock the *mechanism* to refuse such a stamp, while leaving the *policy* with you. It
landed when the identity model it was gated on was defined (`pinax` ADR-0012: per-station
Ed25519 keyed by `station_id`), and that model is now *deployed practice, not design*: both
named consumers run `try_observe` at their trust boundary (`ethos`'s `δέχεσθαι` through
`Producer::try_observe`; `pinax`'s `apply` under its shard lock, mapping `SkewExceeded` to a
typed `LogError` carrying both fields, its S53).

New, additive surface:

[source,rust]
----
impl Clock {
    /// Fold `remote` in as `observe` does, but only if its forward skew over the
    /// current reading is within `max_forward_skew`. A stamp beyond the bound is
    /// rejected and the clock is left unchanged.
    pub fn try_observe(
        &self,
        remote: Kairos,
        max_forward_skew: u64,
    ) -> Result<(), SkewExceeded>;
}

pub struct SkewExceeded {
    pub observed_forward_skew: u64,   // how far ahead of local the remote ran
    pub max_forward_skew: u64,        // the bound you passed
}
----

How to adopt, at the replication ingress (verify identity first, then bound the skew):

[source,rust]
----
// after the Ed25519 signature verifies against the key for remote.station_id():
match clock.try_observe(remote, max_skew) {
    Ok(()) => { /* folded; proceed to merge */ }
    Err(SkewExceeded { observed_forward_skew, .. }) => {
        // your policy: drop, quarantine, or eject the peer
        reject_event(observed_forward_skew);
    }
}
----

Three things to understand:

. *The bound is a property of the call, not of the clock.* `observe` (infallible, unconditional)
  and `try_observe` (bounded, fallible) are two ways to receive into the same clock; you choose
  per call, the same infallible-default / `try_*` escape-hatch shape as `try_insert`. The bound value is
  yours, a runtime argument, so a deployment sets how far ahead a verified peer may legitimately be.
. *Forward skew only, measured against one reading.* The checked quantity is `remote.physical()`
  minus a single reading of the time source, shared by the check and the fold. A remote at or
  behind your reading has zero forward skew and is always admitted (it cannot drag you forward).
  The bound is inclusive. It caps what the *remote* contributes; it does not re-bound skew your
  clock already carries.
. *We own the mechanism; you own the policy, and the trust.* `try_observe` rejects and leaves the
  clock untouched (the receive is all-or-nothing). It does not clamp, log, or eject. Crucially, a
  skew bound only defends when `remote.station_id()` is *authenticated*, else an adversary evades
  it by rotating the claimed identity; minerva counts and gates, it does not authenticate.

*The field recipe (two live citations, a recipe deliberately not a type).* Both consumers
independently hand-rolled the same check order at their receive boundary, which makes it the
documented pattern for the next trust-aware ingress:

. *Authenticity first*: verify the per-event signature against the *claimed* `station_id`'s
  registered key (key-to-station at `ethos`'s deed feed, station-to-key at `pinax`'s ledger
  ingress; opposite lookup directions because opposite boundaries, one `station_id` space).
. *Register / dedup second*: a duplicate is a no-op regardless of provenance, so an exact-match
  pre-check can skip the expensive verification on retries (`pinax`'s `holds_exact`).
. *Bounded observe third*: `try_observe` (clock- or producer-level) in place of `observe`; a
  rejection is inert, no clock movement, no slot spent, no I/O triggered.
. *Capacity / commit last*: `try_insert` or your store's write, so a stamp that will be refused
  never spends a slot.

The ordering is load-bearing: authenticating after the fold would let an unverifiable stamp move
your clock, and committing before the bound would let a refused event leave a trace. A shared
default for the bound value is a *consumer* convention, not ours (`pinax` adopted `60_000`
deliberately from `ethos`'s `ReceptionBounds` so one station's two receive boundaries agree);
minerva keeps the bound a per-call argument.

What does _not_ change: `observe`, `now`, `after`, and every ordering invariant. If you do
nothing, nothing changes.

This is the *reject* mechanism. The *clamp* variant (advance-but-cap instead of reject) is the
deployment liveness/safety policy on the deferred menu below, additive when you choose it.

== Shared-clock `Producer` and the bounded receipt

*Status: Stable. S73 added shared-clock carriers; S291 changes source and carrier types. See
the S291 migration entry below.*

`Producer::new` used to take its `Clock` by value only. That contradicted the station invariant
the crate itself states (one station, one logical clock): a station whose clock also stamps
*non-producer* sites (a seal, a ledger append, all sharing one `Arc<Clock<TS>>`) could not construct
a `Producer` at all without minting a second clock under the same `station_id`, which is exactly
the colliding-stamps hazard the invariant exists to prevent. `ethos` is the caller that hit this
(its `κοινωνία` layer re-rolled `produce` by hand while waiting; see the per-consumer recipe).

Current surface, combining S73's shared-clock capability with S291's concrete-source model:

[source,rust]
----
pub struct Producer<C = DynClock> { /* clock: C, knowledge: VersionVector */ }

impl<C: ClockCarrier> Producer<C> {
    pub const fn new(clock: C) -> Self; // owned Clock<TS>, Arc<Clock<TS>>, or &Clock<TS>

    /// observe(&event), but behind Clock::try_observe's skew bound.
    /// Ok  -> exactly observe (knowledge merged, clock advanced).
    /// Err -> neither changed; the rejected event leaves no partial trace.
    pub fn try_observe<P>(
        &mut self,
        event: &Event<P>,
        max_forward_skew: u64,
    ) -> Result<(), SkewExceeded>;
}
----

How to adopt, for a station with one shared clock:

[source,rust]
----
let clock: Arc<Clock<MySource>> = station_clock();   // the one clock every site stamps with
let mut producer = Producer::new(Arc::clone(&clock));

let event = producer.produce(kairotic, payload);      // broadcast this
// at the ingress, after the sender's signature verifies:
match producer.try_observe(&event_in, max_skew) {
    Ok(()) => buf.try_insert(event_in, cap).map_err(on_overflow)?,
    Err(SkewExceeded { .. }) => reject_peer(),        // producer untouched
}
----

Three things to understand:

. *`C` abstracts ownership, nothing else.* Every operation reaches the clock through the
  sealed `ClockCarrier`, so semantics do not depend on `C`; callers cannot
  substitute a different minting machine under the producer contract. A bare
  `Producer` means `Producer<DynClock>`; inferred construction preserves a
  concrete source instead. `station_id()` is not a `const fn` because carrier
  access is not const.
. *Share the clock across stamping sites, not the producer role.* One station still runs one
  producer. Two producers over one shared clock would each self-count `knowledge[self]`
  independently and mint colliding dots; the type cannot catch this, so it is yours to uphold.
. *`try_observe` is all-or-nothing by design, not convenience.* There is deliberately no
  knowledge-merge accessor that would let you compose `Clock::try_observe` with a manual merge:
  folding a rejected event's `deps` without its stamp would let a later `produce` snapshot
  dependencies its minted stamp does not dominate, breaking the embedding of causal order in
  stamp order. If you need the bounded receipt, take it as one operation.

Runtime semantics do not change: `produce`, `observe`, `knowledge`, composition with `Ideal`, and
every ordering invariant remain the same. Source compatibility does change under S291: inferred
construction produces `Producer<Clock<TS>>`, while a bare `Producer` annotation names
`Producer<DynClock>`. Choose the concrete or erased form, and replace custom `Borrow<Clock>`
wrappers, as described in the S291 migration entry below.

== Causal stability: `VersionVector::meet` and the `Stability` watermark

*Status: Stable after first contact (S78; Alma `93ca2744` / `0a779100`,
independently confirmed by Stokes `1b37f98` / `4615cdd`). No migration
required.*

Any consumer that retains delivered history (retransmission buffers, audit replay, rebuilding a
crashed peer) eventually has to reclaim it, and no retention bound is _safe_ without answering
"what has every peer already passed?" That answer is the *stability watermark* `min_n D_n`, the
meet of the members' delivered cuts: an event at or below it is delivered everywhere, so no member
can ever again need it re-sent. This slice ships the *mechanism*, the third face of the
bounded-resources boundary beside `try_insert` (backlog) and `try_observe` (skew); the forgetting
*policy* stays yours.

New, additive surface:

[source,rust]
----
impl VersionVector {
    /// The greatest lower bound: pointwise min, the lattice meet (dual of merge).
    pub fn meet(&self, other: &Self) -> Self;
}

pub struct Stability { /* roster of station_ids -> join-folded reported cuts */ }

impl Stability {
    pub fn new(roster: impl IntoIterator<Item = u32>) -> Self;   // fixed, declared family
    /// Claim-only door for a bare vector whose evidence stays caller-side.
    pub fn report(&mut self, station: u32, delivered: &VersionVector)
        -> Result<(), UnknownStation>;
    /// Preferred door: join-fold a witnessed, gap-free cut.
    pub fn report_cut(&mut self, station: u32, delivered: &Cut)
        -> Result<(), UnknownStation>;
    /// The n-ary meet of every member's vouched cut. Monotone non-decreasing.
    pub fn watermark(&self) -> VersionVector;
    /// A witnessed watermark when every report used `report_cut`.
    pub fn watermark_cut(&self) -> Option<Cut>;
    /// The per-member slots, ascending; the straggler pinning the watermark is visible.
    pub fn reports(&self) -> impl Iterator<Item = (u32, &VersionVector)>;
}
----

How to adopt, at whatever cadence you already gossip durable application cuts:

[source,rust]
----
let mut stability = Stability::new(members);       // your declared membership set

// Authenticate the peer's stable-before-emit claim, then retain its cut witness.
let peer_cut = Cut::floor_of(&peer_durable_have);
stability.report_cut(peer_id, &peer_cut)?;         // stale/duplicate reports absorbed

let local_cut = Cut::delivered_by(&my_ideal);
persist_local_through(local_cut.as_vector())?;      // caller-owned durability fence
stability.report_cut(my_id, &local_cut)?;

let safe = stability
    .watermark_cut()
    .expect("every report retained its cut witness");
my_store.compact_below(safe.as_vector());           // your policy, not minerva's
----

Three things to understand:

. *The roster is the meaning, and it is yours.* A meet is only as trustworthy as the family it
  ranges over, so the roster is fixed at construction (a constructor argument, like `capacity` and
  `max_forward_skew`) and non-roster reporters are refused. The two errors this makes
  unrepresentable are the ones that lose data: meeting over "peers heard from so far"
  (over-approximates stability whenever an unheard member is behind) and the empty family's
  vacuous "everything is stable" (the empty roster answers bottom instead). Membership _change_ is
  a policy: a joiner must regress the watermark, a leaver un-pins it, and both re-define "stable",
  so you handle them by building a new tracker over the new roster.
. *The watermark never regresses, which is your license to act.* Reports fold by join, so
  duplicated, reordered, or stale gossip is absorbed and `watermark()` is monotone non-decreasing.
  The cost is the meet's availability profile: a member that never reports pins the watermark at
  bottom, and one that stops reporting pins it at its last cut. That is inherent (stability _is_
  unanimity), and `reports()` exposes who is pinning; paging or evicting the straggler is your
  call.
. *Mind hazard 2: report delivered cuts, not observed knowledge.* The meaningful input is an
  application-delivery cut (an `Ideal`'s `delivered()` / `progress()`, or your own consumer's
  equivalent), because "safe to forget" means *applied everywhere*, not *received everywhere*. A
  `Producer`'s `knowledge` is receipt-level and generally runs ahead; feeding it to `report` would
  declare stability for events a peer has received but not yet delivered. And a report must be a
  *genuine cut*: a gap-free per-station prefix (PRD 0011 R8). Minerva's own vectors are gap-free by
  construction, but a max-style high-water over an out-of-order store can run *past holes* (hold
  `{0, 5}`, publish `6`); that is an upper bound of a cut, and meeting upper bounds over-claims
  stability, so close the gaps (or track an exact have-set) before reporting. The exact have-set
  is in-tree since S83: `DotSet` (PRD 0012) records disordered receipt exactly, its `floor()` is
  a cut by construction (the honest report), and its `high_water()` is precisely the quantity
  that must never be reported.
. *A report is a durability claim too (S264, ruling R-51).* The same join that absorbs stale
  gossip absorbs a crash-restart regression silently: if you report a delivered cut, crash
  before persisting it, and restart lower, every peer's tracker keeps your higher pre-crash
  claim while you can no longer honor it, and the watermark may already have licensed
  irreversible action on it. Report the floor of what you have durably stored, not of what you
  have delivered; the executable recovery recipe (fence before emit, checkpoint at the seal,
  replay plus anti-entropy) is the fleet replica's, `src/metis/tests/fleet/`.

A derived read worth knowing: the same reports also fold the other way. The pair
`(meet, join)` of the roster's cuts brackets the system (everything below the meet is
everywhere; nothing above the join is anywhere), and the per-station gap between them is a
natural replication-lag window, computable today with the two folds you already have. Minerva
ships no metric type for it, since which norm over the window matters is yours; if you find
yourself computing it, say so, because that interest is a signal.

First contact arrived through Alma's committed certified-view transport on
2026-07-20. A `ViewCertificate` defines the fixed roster, the caller durably
persists its own `Cut` report table before publishing, one `InstalledView` owns
one tracker across every document carrier, and restart rebuilds it through
`report_cut`. The consumer uses `watermark_cut` for Minerva's epoch lifecycle
license and keeps certificate authority, incarnations, persistence failure,
transport, roster replacement, and refoundation policy outside Minerva. The
surface held without a bulk constructor, tracker codec, or merge operation, so
PRD 0011 R7 is satisfied.

Stokes independently reached the same boundary through a different application
shape. Its draining lineage runtime privately owns the fixed-roster tracker,
persists bounded membership-bound witnessed-cut rows, rebuilds through
`report_cut` on restart, and permits durable terminal retirement only after the
witnessed watermark covers the proposal frontier. Stokes keeps credentials,
codec policy, persistence, retries, and retirement semantics. It also needed no
tracker-level persistence or merge API.

What does _not_ change: every existing type and method, the wire frames, and every ordering
invariant. The tracker has no wire form of its own (gossip your cuts as PRD 0007 `VersionVector`
frames); it is local state you feed. If you do nothing, nothing changes.

This is the mechanism half of the long-standing cross-node-meet menu item (S29b, PRD 0011, built
under owner ruling R-6). The policy halves, reclamation and membership change, stay on the
deferred menu below.

== Relative reads: restriction and the exact glue

*Status: Stable (S89, xref:prd/0013-metis-relative-base-change.adoc[PRD 0013], owner ruling
R-10). Additive reads on the existing types; if you do nothing, nothing changes.*

Both lattice types now answer questions *relative to a sub-roster*, and vectors can be glued
from partial views *exactly* instead of laxly:

[horizontal]
`VersionVector::restrict(roster)` / `DotSet::restrict(roster)`:: The same causal record, read
  as far as the named stations are concerned; everything else reads absent. Restriction
  commutes exactly with every fold and read you already use (`merge`, `meet`, `floor`,
  `high_water`, `holes`, `from_cut`), so it composes freely: restrict-then-fold equals
  fold-then-restrict, always.
`VersionVector::try_glue(&other, overlap)`:: The `merge`, guarded: checks that both sides
  agree on every station in `overlap` first, and refuses with a `Disagreement { station,
  left, right }` instead of folding when they do not.

Two disciplines make these safe, and both are yours to hold:

* *Restricted order is order over the sub-roster, never globally.* If two restrictions are
  *concurrent*, the originals genuinely are (trust that verdict). But two concurrent vectors
  can restrict to *ordered* ones, because the roster you dropped may be exactly where they
  disagreed. Never compare restrictions and act as if the verdict were global; that
  fabricates a happens-before out of forgetting (the created-order hazard, and the reason
  `restrict` exists as a documented read rather than a pattern you hand-roll).
* *Glue authority; merge knowledge.* `merge` resolves every disagreement by maximum, which
  is right when a bigger count is simply newer news. It is wrong when two partial views each
  *claim to speak for* a station: merging their disagreement fabricates a cut nobody
  reported, and a fabricated cut fed to `Stability::report` over-claims in the unsafe
  direction (the same trap as reporting a high-water, one step removed). Declare the overlap
  (which stations both views claim; a deployment fact, like the roster) and `try_glue`; on
  `Disagreement`, deciding who over-claims, or falling back to `merge` deliberately, is
  yours.

The natural first uses: scoping any vector question to a shard or working set
(`restrict` then compare, holding the discipline above); and assembling one honest cut from
views that arrive in sub-roster pieces (`try_glue` piecewise, then `floor`-and-report). If
your reports genuinely cover sub-rosters as a matter of course (a relay topology), say so:
the n-ary fold over a declared cover is chartered and waiting on exactly that signal
(PRD 0013 R7).

== Difference reads: what do I owe you

*Status: Stable (S95, xref:prd/0014-metis-difference-reads.adoc[PRD 0014], owner ruling
R-11). Additive reads on the existing types; if you do nothing, nothing changes.*

The anti-entropy question between the folds you already have: `merge` is what we jointly
know, `meet` is what we agree on, and `difference` is what I owe you.

[horizontal]
`VersionVector::difference(&other)`:: The *owed set*: the least vector whose `merge` into
  `other` covers `self` ("the least gossip that makes you cover me"). Empty exactly when
  `other` already dominates you; merging it into `other` lands exactly on the join, no
  more. It commutes with `restrict` ("what do I owe you about the stations you track").
`DotSet::difference(&other)`:: The same question at dot granularity: a lazy, ascending
  `(station, dot)` enumeration of exactly the dots you hold that `other` lacks, the serve
  list dual to `holes()`'s fetch list. Serving them converges the peer to the union;
  between genuine cuts it enumerates exactly the spans the vector read names.

One shape discipline is the whole hazard, and it is worth stating because the wrong code
type-checks: *the residual carries claims, never subtracted counters*. Where you lead 5 to
3, `difference` answers 5 (the count the peer must be raised to), not 2; the owed events
are the dots in the span `(3, 5]`. If you hand-roll `a(s) - b(s)` anywhere, the numbers
still look like a vector, but they name the wrong dots, and a repair loop built on them
re-serves your oldest events forever while telling you it is converging. The read exists so
that trap does not (the adjoint ledger's shape warning, pinned by example test upstream).

What stays yours: everything around the read. Who asks whom, when, over what transport,
batch sizes and caps (bound the enumeration with `take` or your own budget), and what a
count *means*. The vector read computes over claims: differencing against a liveness
high-water tells you what the peer's *horizon* lacks, which over holes is less than what
the peer actually lacks; difference against a floor (or an exact have-set) when repair
below the horizon matters. That is the same cut-versus-upper-bound discipline as the
stability section's, one read over.

== The causal store: replicated state that can remove

*Status: Stable (S96, xref:prd/0015-metis-causal-store-algebra.adoc[PRD 0015], owner ruling
R-12). Alma supplied first contact through `Composer<Rhapsody>`; Polis confirms the in-tree
`DotMap<K, DotFun<V>>` composition, and Stokes `0a14aae` supplies the first external custom
`DotStore`, an inverse-indexed effect store checked against the canonical `DotMap` result.*

If your replicas only ever add, the joins you already have converge you. The moment you
*remove*, a plain union resurrects everything any stale peer still holds, and the classic
patches are both bad materials (tombstones grow forever; last-writer-wins drops concurrent
writes, which in a collaborative editor is a user's keystrokes). The causal pair is the
lattice answer:

[horizontal]
`Dotted<S>`:: Your state *and* your deltas, one type: a store `S` of dot-tagged content
  plus the context of every dot ever seen. `merge` is the only write. A dot survives a
  merge iff both stores hold it, or one holds it and the other's context never saw it;
  "in the context, not in the store" means seen-and-removed, and the merge honors it.
`DotStore`:: The open axis (the `Gate` posture). Minerva ships `DotSet` (bare presence),
  `DotFun<V>` (a value per dot under the equal-by-basis discipline), and `DotMap<K, S>`
  (per-key composition, nesting to any depth, your `K` and `V` never interpreted). Implement
  `DotStore` for a different store shape against the five trait laws; your payload semantics
  never enter minerva. Stokes's indexed effect store is the field example: its payload and
  inverse-support invariant stay local, while `causal_merge_from` overrides only the cost of the
  same survivor law.

The write flow (the merge records the dot; nothing else does):

. `let dot = live.next_dot(my_station);` assignment reads the *context*, so it stays
  fresh across removals. Merge each write's delta before assigning the next.
. Build the delta: `Dotted::from_store(DotMap::singleton(key, {dot}))` for an add;
  `Dotted::from_context(observed_dots)` for an observed remove (supersede exactly what
  you saw, so an unseen concurrent add survives: add-wins is *your delta's observation*,
  not a merge mode); `Dotted::try_new(store, context)` when a write also supersedes (a
  register-style update), refused with an `UncoveredDot` witness if the parts lie.
. `live = live.merge(&delta);` then ship the delta (or the full state; they are the same
  type and any merge order converges). Persist before you send: a delta that escapes
  before it is durably merged locally can, after a crash, cost you a dot reassigned to
  different content.

Two disciplines, both yours: what a *removal observes* is your semantics (observed dots
for add-wins; unobserved dots is a remove-wins posture; minerva never chooses), and
*display order* among concurrent survivors is yours to rank (the natural discriminator
is the `Kairos` stamp you already carry, kairotic tag included). For an editor
specifically: your sequence elements' identifiers *are* dots, so the kernel gives you
identity, causality, and removal, and you keep ordering, payloads, and the sequence
structure itself in your own `DotStore` impl.

== Breaking changes already landed

If you are pinned to an older `minerva` revision, apply these migrations on
upgrade. Each change is deliberate. Each row states its behavior impact.

[cols="1,3,3",options="header"]
|===
| Slice | What changed | Migration

| S348 / 0.2.0 | The single-variant `ClockError` enum is replaced by the
  `InvalidStationId` unit struct. All four clock constructors return this
  error when `station_id` is zero. Validation, state transitions, and the
  constructor-produced error's `Display` text are unchanged. The old enum let
  callers construct the variant with a custom static string. The unit struct
  removes that presentation hook.
| Replace `ClockError` imports and `Result<_, ClockError>` annotations with
  `InvalidStationId`. Replace `Err(ClockError::InvalidStationId(_))` patterns
  with `Err(InvalidStationId)`.

| S291 / 0.2.0 | `Clock` and `LocalClock` now carry their owned
  `TimeSource` as a type parameter. Concrete construction is allocation-free;
  `DynClock` and `DynLocalClock` name the erased forms. `Producer` accepts the
  sealed `ClockCarrier` capability so concrete, borrowed, and `Arc` clocks all
  preserve one station clock without erasing its source.
| Prefer `Clock::with_default_config(MySource::new(), station)` and let `TS`
  infer. Where one field must hold heterogeneous sources, spell
  `DynClock` and pass `Box<dyn TimeSource>`. Apply the same choice to
  `LocalClock`. Existing `Clock` type annotations continue to mean the erased
  default; make `Clock<MySource>` explicit when storing a concrete clock. A
  custom wrapper that previously implemented `Borrow<Clock>` is no longer a
  producer carrier: pass an owned clock, `&Clock<TS>`, or `Arc<Clock<TS>>`
  directly. `ClockCarrier` is sealed so wrappers cannot redefine the producer's
  clock capability.

| S266 | The seal bequest is renamed the seal *consignment* (an owner naming
  decision; Latin _consignare_, to mark under seal, the door's own witnessed
  precondition): `EpochShadow::bequeath` is now `EpochShadow::consign`,
  `EpochBequest` is `EpochConsignment`, and `EpochBequestError` is
  `EpochConsignmentError`. Signatures, semantics, refusal variants, and wire
  contracts are unchanged, and no consumer had adopted epochs at the rename.
| Rename the identifiers at your call sites; nothing else moves. If you had
  pattern-matched `EpochBequestError` variants, only the enum's name changes,
  never the variants.

| S199 | `Rhapsody::locus` and `Recension::locus` return `Option<Locus>` by value
  instead of `Option<&Locus>`: the identity plane run-coalesced (arc 11 phase two,
  ruling R-26), so a chain-interior element stores an inline rank step rather than a
  `Locus` and the read materializes its answer. `Locus` is `Copy`; wire bytes,
  ordering, and every other read are unchanged.
| Usually nothing: a binding read like `let Some(locus) = store.locus(s, d)` followed
  by field access compiles identically (verified at alma's one call site, its
  `weave_char`: `cargo check` and its collab suites ran green against the S199 tree
  unchanged). Only a pattern that dereferences (`Some(&locus)`) or stores the
  reference itself needs the `&` dropped.

| S32c | The producer's learn method `Producer::deliver(&event)` was renamed
  `Producer::observe(&event)`: it is *receipt*, not application delivery (in
  Birman-Schiper-Stephenson "deliver" is the buffer's `pop_ready`). Same signature and behavior.
| Rename the call site `producer.deliver(&e)` -> `producer.observe(&e)`. No behavior change.

| S27b | The gate seam. `Ideal<T>` is now `Ideal<T, G: Gate>` with *no default gate*;
  `Event<T>` is now `Event<T, D = VersionVector>` (the default keeps `Event<T>` valid).
  `Ideal::delivered` is `Causal`-only.
| Name the gate: `Ideal<T>` -> `CausalIdeal<T>` (or `Ideal<T, Causal>`). For per-source FIFO
  delivery use `FifoIdeal<T>` (`Event<T, u64>`). Read generic progress through `progress()`;
  `delivered()` stays for the causal case. No behavior change on the causal path.

| S26 | The delivery buffer was renamed. `CausalBuffer<T>` -> `Ideal<T>`; its frontier reader
  `antichain()` -> `frontier()`. "Causal" was de-emphasized as branding (the mechanism is still
  causal broadcast).
| Rename the type and the method at your call sites. No behavior change (byte-for-byte the same
  gate, linearization, and frontier set).

| S21 | `Producer::latest()` (getter and field) was removed; the clock is now the single
  high-water. An `observe` now ticks the clock once (the HLC receive rule), so a later produced
  stamp can land a logical step higher than before.
| Drop any use of `latest()`. Assert stamp _relations_ (monotonicity, dominance), not absolute
  logical values; the free-running logical tiebreak carries no meaning and may shift.
|===

The `Kairos` field layout (`kairotic` above `station_id`) and the wire frames (PRD 0002, PRD
0007) are the load-bearing compatibility surfaces. A change to either is a wire-and-ordering
break and would be called out as such; none is planned. Since 2026-07-02 the stamp frame carries
a second, stricter obligation: both field consumers embed `Kairos::to_bytes()` in Ed25519
signature *preimages* and recompute the frame at verification (`pinax`'s
`pinax/event/ed25519/v1` provenance message, `ethos`'s seal's `κανονικὸν_μήνυμα`), so byte
*identity*, not just byte order, is contract. A future frame version would invalidate every
stored provenance and seal minted over v1, record-carried authenticity ecosystem-wide, not a
codec migration; any v2 ships only with advance notice here and in both consumers' feedback
files, so stored records can pin v1 bytes in their preimages or re-sign first.

The `VersionVector` frame carries the same class of obligation since `pinax`'s anti-entropy
session v1 (its S57, 2026-07-02): every session body opens with this frame, split off by
`from_prefix` from network bytes, so the session protocol transitively pins frame v1 and a
frame v2 would be a session *wire break*, not just a new durable format. The absorption path
exists by construction (the session frame carries its own version byte, so a coordinated bump
rides a session v2), but the notice discipline is identical. Both pins are instances of the
standing rule R-8 in `owner-comments.adoc`: a frame a consumer embeds in a preimage or a wire
protocol is byte-identity contract, the crate's fuzz coverage follows the embedding's trust
class (S85 added `envelope_from_prefix` for exactly this seam), and a frame version bump is a
coordinated ecosystem event, never a unilateral one.

`DotSet` v1 joined that registry with Polis S3 (`5d10141`): Polis possession
protocol v1 length-delimits the canonical have-set beneath its authenticated
envelope and passes a receiver-owned exception ceiling to
`from_bytes_with_budget`. A `DotSet` v2 would therefore be a Polis possession
protocol break. Minerva pins the canonical inner bytes and allocation bound;
Polis pins the outer length, signature, charter, and admission behavior.

*Second frame-embedding shape observed (2026-07-17, alma TRANSPORT-2
worktree, ruling R-55), the Polis S3 handling applied again:* alma
transport v1 would be the first protocol embedding of the *rhapsody v2
skeleton frame* anywhere, plus the have-set frame as claim and context
sections and the 17-byte `Kairos` stamp inside its movement rows, all
beneath a BLAKE3 keyed-MAC envelope (MAC verified before any structural
read, minerva's exception-dot budgets at every have-set, an outer frame
cap refusing oversized declarations off the header alone). The
inspected alma tree is uncommitted atop `3afd6c4e`, so exactly as with
Polis S3, R-8's protocol pin, the coordinated-bump duty, and the
composed-parser coverage obligation *wait for its landed commit* rather
than being claimed early; the reply letter (local, untracked) asks alma
to name it. What did land now, as standalone decoder coverage rather
than the composed-path obligation: the v2 frame decoder's
adversarial-bytes fuzz target (`rhapsody_from_bytes`) beside its
standing never-panics proptests.

*Those pins fired (2026-07-18, S280, ruling R-62): the embeddings are
landed.* Alma `83fb666c` lands the authenticated carrier with the
rhapsody v2, have-set, and `Kairos` stamp embeddings exactly as
observed, and alma `764d8b03` adds the *snapshot v1 frame* (PRD 0023)
under an authenticated selector byte, admissible only against a
correlated whole-store fresh claim (empty text context, woven witness,
and visible witness), with the S277 `snapshot_encoded_len` preflight as
the pre-serialization cap. Rhapsody v2, snapshot v1, `DotSet` v1, and
`Kairos` v1 are therefore *protocol contract for alma transport* under
R-8: a version bump on any of them is a coordinated alma wire event,
the composed-parser coverage rides alma's transport falsifier suite
(which mutates canonical frames through the authenticated envelope),
and minerva's standalone fuzz targets remain the inner-decoder floor.
At this S280 cut the `Metatheses` v1 and epoch-ledger v1 entries still
waited: the deltas-v3 movement adoption and authenticated checkpoint
wrapper were uncommitted. Their later landed pins are recorded below.

*The movement-record frame those rows anticipated now ships as
minerva's own codec (S272, ruling R-56):* `Metatheses::to_bytes` /
`from_bytes` / `from_bytes_with_budget` under `METATHESES_WIRE_V1`,
rows in strictly ascending testimony-dot order spelling `(testimony
dot, target dot, sided anchor, 17-byte kairos rank)`, alma's field
order under minerva's big-endian discipline (the two spellings agree on
fields and diverge on byte order; alma retires its little-endian
version-zero section on adoption). Canonical form is enforced (byte
identity is value identity; zero-index targets and anchors are
representable payload and round-trip), the testimony budget is the
caller's (`MetathesesDecodeBudget`, the have-set precedent), refusals
are layered (`MetathesesDecodeError`), and coverage is the full R-9
split: the golden layout fixture, shaped proptests, and the
`metatheses_from_bytes` fuzz target. Since S280 (ruling R-62) the
codec also owns its exact length: `Metatheses::encoded_len()` is the
allocation-free pre-serialization preflight (the R-59
`snapshot_encoded_len` shape), so a carrier enforcing an authenticated
payload cap never re-derives the row widths downstream; the wire
property suite pins it equal to the emitted frame.

*The metatheses v1 pins fired (S281, ruling R-63): alma `b150a2b8`
lands deltas kind v3 on this frame.* Alma's provisional movement body,
version tag, parser, and byte order are retired; structural refusal is
delegated to `Metatheses::from_bytes` with the typed cause surfaced
through their `DeltaDecodeError::Metatheses`; their nonzero-dot
narrowing runs after minerva's exact decode as embedding policy; and
`5aebe4d5` adopts `encoded_len`, deleting their duplicate length law.
Metatheses v1 is therefore alma-transport protocol contract: a version
bump is a coordinated wire event, and the composed-parser coverage
rides alma's envelope falsifiers (which mutate the canonical frame
through the authenticated MAC). The *refound-map v1* frame is
tuple-pinned at the same commit: the frozen protocol tuple and the
lineage recipe's `M` field name it, so its bytes are contract, with
the first byte-level embedding arriving with the boundary-lineage
code. The frozen joint registry itself is tracked as PRD 0025
(minerva's half) beside alma's `epoch-transport.adoc`. The epoch-ledger
v1 entry fired later when TRANSPORT-15 placed its canonical bytes in
Alma's durable owner record; that storage boundary is described below.

*The five lifecycle record frames landed as S284 (ruling R-67; PRD
0025 section 6, kinds 5 through 9):* `Declaration::to_bytes` /
`from_bytes` / `encoded_len` under `DECLARATION_WIRE_V1`, and the four
new report-record types `ConfirmationRecord`, `AdoptionReportRecord`,
`SealRecord` (self-delimiting: `from_prefix` beside `from_bytes`, a
`SealDecodeBudget` capping each retained set in the candidate unit),
and `LineageProofRecord` / `LineageProofEntry` (consecutive
generations enforced at `try_new` and decode alike, a
`LineageProofDecodeBudget` carrying the generation ceiling plus the
per-seal candidate budget threaded into every embedded frame), each
with its own version namespace, typed error, and exact `encoded_len`
preflight. Decoded forms are report records, never witnesses (the
S273 rule): a decoded `Declaration` feeds `Epochs::deliver`, which
re-validates everything; a decoded `SealRecord` is a peer claim
compared against `SealRecord::from_sealed` of your own seal; nothing
decodes into `Adopted`, `SealedEpoch`, or a live machine. An
`EpochAddress` identifies a winning declaration, not the full record:
comparing addresses across members does not compare records. Arrival
endorsements compare their caller-supplied commitments over the exact seal
frame when record identity is required. Every embedded cut rides as a
canonical gap-free have-set whose gap-freedom
the receiver proves (a gapped embedding refuses as
`WireCutError::Gapped`; the decoded cut enters through
`Cut::floor_of`, a peer claim in witnessed shape). The seal frame
carries the window's *two distinct retained sets* (retired candidate
addresses and the protocol declaration-dot ledger), and the golden
fixtures pin a displaced-declaration window whose ledger keeps a dot
the candidates lost (the PRD 0025 R4 obligation). Coverage is the R-9
sampled-plus-fuzzed split: golden fixtures, shaped proptests, and five
fuzz targets. The kind 5 through 7 registry pins fired at alma's
landed embedding commit `4060ab6b`; kinds 8 and 9 fired at `806d4ecf`,
where TRANSPORT-17 admits the checkpoint pair only after Session Hello
v3's bilateral fence and retires the predecessor carrier after
successor installation. All five Minerva lifecycle rows are protocol
contract. Alma `1454e905` adds the distinct Byzantine profile without
respelling them: crash-only retains Hello v3 and kinds 1 through 9;
Byzantine Hello v4 binds the exact `ConfigurationId` and adds kind 10 v1,
a fixed 205-byte checkpoint attestation. Kind 10 is Alma-owned and is not a
sixth Minerva lifecycle frame. The epoch-ledger v1 storage pin fired
separately at alma `f37c3245`:
TRANSPORT-15 persists and restores Minerva's bounded canonical ledger
in a private, atomically replaced owner record. On a pending boundary,
Alma separately verifies the certificate-bearing checkpoint, requires its
delta generation to equal the ledger generation, and binds its lineage to
the separately stored proof. The local ledger bytes are not covered by the
checkpoint certificate, so neither section is authority alone. Alma
`a813c4ee` compares the proof's retained seals with the rehydrated ledger on
every owner-state write and restore before replay or authority escape.
Checkpointed states require exact bounded-history equality. The pending
durable-seal window permits exactly one newest ledger seal beyond the proof,
with generation and horizon trimming checked explicitly. Restore refuses a
mismatch as `EpochWindowInstallError::LedgerProofMismatch`. Alma `9d035920`
uses one relation classifier on both paths and pins exact and pending
canonical-section splices. TRANSPORT-20 at `a6b9e29d` then advances Alma's
private aggregate from `ALMAEW04` to `ALMAEW05`: one closed lifecycle replaces
independent pending/checkpoint fields, and a successor handoff retains the
exact predecessor batch plus one durable receipt per immutable-roster slot.
The complete successor image is encoded and bounded before the installed view
advances; view-first recovery reconstructs the same handoff. This is an
Alma-owned aggregate migration. Epoch-ledger v1 and every wire frame remain
byte-identical. Wire and storage pins do not follow the crate version; each
requires its own coordinated migration.

*The joint boundary fixture is bilateral (S327, ruling R-77):* alma
`806d4ecf` owns the BLAKE3 tuple calculation and pins the worked
two-station boundary's `P`, `S`, `M`, `A`, and `N` rows. Minerva checks
in their provenance-stamped export, compares all five rows to independent
byte arrays, and compares its lineage encoder against a manually assembled
frame with `N` at the exact `LINEAGE_PROOF_WIRE_V1` offset before decoding
that independent frame. This proves the owned codec preserves the caller's
lineage coordinate without importing Alma's glyph, digest, or attestation
domain. Alma `1454e905` leaves the worked crash-only fixture source unchanged
and separately runs a real-socket Byzantine window: both sides complete the
fence, persist before successor installation, carry the ordered seal,
attestation, checkpoint triple, verify the station claim under the exact
session roster against locally durable truth, and reopen durably. This B4
evidence is carriage, not a quorum certificate or freshness decision; those
remain Alma B5 and B6. Alma's first non-empty transported window then landed at
`49c11873`: all nine event kinds crossed, one duplicate replayed, and
the boundary stayed off the interaction thread. Alma `a6b9e29d` and
`728aa945` subsequently complete the RECEIPT-backed crash schedule. A fresh
process reopens the installed views and `ALMAEW05` owners, each exact owner
mints a move-only `DurableCheckpointRecovery`, and the headless adapter
consumes it without replaying checkpoint text as local authorship. Successor
Hello releases nothing; authenticated successor state durably receipts the
exact peer before semantic admission, roster completion opens both owners,
and new edits from both recovered stations converge with dots above their
restored histories. S330 records this caller-owned capability and carrier
proof without adding a recovery wire kind, second snapshot codec, or Minerva
API.

*The kind-9 receiver has its machine door (S292, ruling R-74):*
`Epochs::bootstrap(roster, horizon, &LineageProofRecord)` constructs a
joiner's lifecycle machine from certificate-verified proof entries,
installing exactly the duplicate-recognizer horizon (the retained
lineage plus the next window generation, the newest seal's successor)
and nothing else. The trust decision is yours and comes first: verify
your checkpoint certificate before this door, because the door
re-proves only the machine invariants (roster-minted retained sets
and sealed-join support, winner among its candidates, ledger covered
by the sealed join, candidate generation homogeneity) and refuses
with the typed `EpochBootstrapError` vocabulary (the empty proof, a
proof past your horizon, the ceiling-adjacent generations, and the
per-entry violations). What
you get is agreement: a bootstrapped machine equals one that lived
through the sealed generations, so `recognize`, replay absorption, and
the next lifecycle round behave identically, while adoption authority
is deliberately not restored, `Adopted` is re-earned live through
`adopt`. Fresh join keeps its one door (`Epochs::new`); trimming an
over-horizon proof to its newest suffix is your visible act.

*The live machine now mirrors the snapshot's read surface (S294, ruling
R-76):* four O(1) reads answer the questions a returning or recovering
member used to have to materialize a checkpoint for. `Epochs::generation`
(the next window's lineage generation, advanced only at seal),
`Epochs::roster` (ascending station ids), and `Epochs::horizon`
(the retained-recognizer bound) are the live twins of the snapshot's
configuration reads, so a *config-versus-proof* check reads the machine
directly: validate an incoming `LineageProofRecord` or certificate against
your configured family without decoding a snapshot to learn three numbers.
`Epochs::newest_sealed` returns the newest retained `SealedEpoch` by
reference, and carries two duties on one door. First, the *freshness floor*
for a returning member, stated in checkpoint terms because an installable
checkpoint names its *successor* generation while a seal names its
completed one: before authenticating a checkpoint and passing its verified
`LineageProofRecord` to `Epochs::bootstrap`, refuse any checkpoint whose
successor generation is at or below your newest seal's, equivalently below
live `generation()`. An equal-labeled pre-seal checkpoint must never roll
you back across your durable seal. Second, the
*crash-between-seal-and-checkpoint* re-entry pivot: if you crash after
persisting your sealed ledger but before minting your bootstrap checkpoint,
rehydrate (Minerva re-proves the retained lineage whole), read
`newest_sealed`, and re-wrap that recognizer under your fresh incarnation
behind your own checkpoint-pending guard, then recompute the boundary state
deterministically and re-enter your existing mint. Minerva supplies only the
read: `try_seal` stays the one live door, and the affine `Adopted` witness
is re-earned through `adopt`, never reconstructed from bytes. `SealedEpoch`
also now exposes `candidates` (every retired candidate address, ascending),
restoring symmetry with its wire twin so a certificate layer binds over both
retained sets from the live entry without cloning a record. One codec edge
hardened with these reads: snapshot decode now refuses a sealed join
covering a station outside the roster (`InvalidState`): impossible for a
live seal and refused by the bootstrap door, but previously installable by a
crafted local checkpoint, where it would have handed `recognize` verdicts to
foreign traffic.

*The rename map has its canonical frame too (S278, ruling R-60):*
`RefoundMap::to_bytes` / `from_bytes` / `from_bytes_with_budget` under
`REFOUND_MAP_WIRE_V1`, one row per ceiling station spelling `(station,
cut ceiling, live count, then the old-epoch indices whose one-based
position is the new index they compact onto)`. This is the byte
carrier for the epoch boundary's old-to-new identity commitment, the
piece the S275 state-identity research found missing from every
published system: if your payload re-keys through the map (glyphs,
cursor anchors, undo records), the lineage node's rename digest will
bind these bytes, so two stacks that disagree on the map become
auditable instead of silently misassociating payload. Two things to
internalize before re-implementing: entry order is *semantic* (the
position carries the new index, so permuted entries are a different
map, both canonical), and a fully swept station still rides as a
zero-count row because its ceiling bounds the affine window
translation. Budgets are yours (`RefoundMapDecodeBudget`: station rows
and cumulative entries), refusals are layered
(`RefoundMapDecodeError`), and coverage is the full R-9 split (golden
fixture, shaped proptests over maps earned from real `refound` folds,
the `refound_map_from_bytes` fuzz target). The digest function,
preimage composition, and this frame's registry entry are the S274
joint session's to settle; the bytes are stable now.

*The family's byte discipline is big-endian, permanently (ruling
R-58).* If you re-implement a frame in another stack, expect network
byte order everywhere, and expect it to stay: byte order is not a
preference here but a property. Big-endian fixed-width integers make
memcmp order equal numeric order, so an encoded `Kairos` stamp sorts
*causally* as an opaque byte string in any byte-keyed store without
linking the codec (the stamp rustdoc's founding claim, frozen into
signature preimages at two consumers), and numeric dot rows encoded in
canonical ascending order remain memcmp-ascending for free.
Your little-endian host pays one `bswap` per field inside a validating
parser that touches every field anyway; there is nothing to win by
asking for a native-order variant, and no knob will ship (a
configurable order would give one value two encodings, which kills the
canonical bijection your content hashes, MACs, and the future lineage
digests rest on). Practically: take byte order from the golden
fixtures, which read left-to-right as place value, and if your
platform genuinely needs a native-order *at-rest* record format some
day, that is a new sibling codec conversation under R-8, not a
parameter on these frames.

Since S86 the assurance behind these pins divides by tool fit (ruling R-9). The `Kairos`
frame's laws, the ones your signature preimages rest on, are *proven totally*: round-trip and
sorts-causally (PRD 0002 R6/R2) are discharged by Kani for every pair of stamps, so the bytes
you sign over are backed by a proof, not a test corpus. The `VersionVector` parser's
map-touching laws (accept-path bijection, canonical-form rejection) are owned by *shaped*
property tests plus the two fuzz targets, with Kani kept to the parser's heap-free fragment
(length arithmetic, sub-header rejection): symbolic map structure is out of a bit-blasting
solver's affordable scope, and Creusot is the chartered deductive rung if a consumer ever
needs the stronger claim there.

== Per-consumer integration

Concrete recipes for the consumers on the tree today. Each depends on `minerva` *directly*;
none depends on another through `minerva` (an `ethos` endorsement ledger backed by `pinax` is
an `ethos`<->`pinax` integration, with `minerva` as the common substrate, not an intermediary).
Each recipe is an instance of "pulling a menu item": the use case, the surface that serves it, and
the trigger that un-gates the rest.

=== `plethos` / `pinax` (the durable ledger and replication)

*Keep the ledger `metis`-free.* The append-only ledger surface (`Archive`, `EventHandle`,
`Event`, `Anagraph`, `LogError`) need not touch a `VersionVector`; only the replication path
(`Horizon`, `ReplicationPort::synopsis` / `delta`) reads `metis`. That split is worth preserving in
your types, because a ledger sequence and a causal vector carry different meanings over the same
`station_id` space (hazard 2 below).

*Per-source FIFO is the `Fifo` gate, held in reserve.* Read against your live source (2026-07-02),
your shipped apply path deliberately needs no holdback: `merge_remote` admits events in any order
(dedup by exact `Kairos`, order recovered by re-sort at read), and your `Horizon` is a liveness
high-water rather than an exact have-set. Do not adopt a buffer you do not need. The moment a
refinement *does* force per-source ordered application (your R9 discussion names the fork: a
reorder buffer or the DVV), the reorder buffer already exists and is exactly `FifoIdeal<T>` over
`Event<T, u64>` with `dep = sequence + 1` (your ADR-0010 interlock): `deps` is the sender's bare
`u64` sequence number, `progress()` is the per-sender watermark map, and release is per-source FIFO
with the same `Kairos` linearization of the concurrent frontier as the causal path.

[source,rust]
----
let mut buf: FifoIdeal<Record> = Ideal::default();
buf.insert(Event { stamp, deps: seq /* u64 */, payload: record });
while let Some(record) = buf.pop_ready() { apply(record); }
----

The send-side mirror (`metis`-blessed minting / framing of `Event<P, u64>`) is *deferred* (S27c,
PRD 0009 R7): rolling a per-source `u64` counter yourself is trivial, so frame the scalar `deps`
yourself for now (the `VersionVector` wire codec is causal-specific).

*The synopsis / delta is the `VersionVector` wire codec.* Frontier reconciliation is
`VersionVector::merge` (the join) and `partial_cmp` (are two frontiers equal, ordered, or
concurrent). On the wire, `to_bytes` / `from_bytes` / `from_prefix` is the canonical,
bounded-allocation, fuzz-tested digest frame: equal knowledge encodes to identical bytes (a sound
dedup key), and decode validates canonical form on untrusted input. `from_prefix` splits a digest
off the front of a framed message and hands the tail back untouched, the same seam the `Event`
envelope uses.

*One trigger has fired; one is still gated.* Your replication design moved two menu items:

[horizontal]
Trust-aware receive (S24):: *Now built and field-adopted* (your S53, verified at
  `replication.rs`): at `apply`, after per-event Ed25519 verification against the claimed
  `station_id`'s registered key, `Clock::try_observe(kairos, max_forward_skew)` runs under the
  shard lock in place of `observe`, before the reservation and anagraph write, with
  `SkewExceeded` mapped to your typed `LogError::SkewExceeded` carrying both fields. That makes
  you the second of two field consumers of the mechanism (with `ethos`'s `δέχεσθαι`), and your
  wiring confirmed the load-bearing properties in anger: the all-or-nothing inert rejection, the
  inclusive bound, and the single-reading coupling of check and fold. The bound stayed
  caller-side where it belongs (your per-archive tunable, defaulting to `60_000` adopted from
  `ethos`'s `ReceptionBounds`). You had already *decided reject and declined clamp* (ADR-0012): a
  clamp would record a far-future stamp your clock never reaches, breaking the
  clock-dominates-stream invariant that lets a concurrent `append` `push`. Nothing further is
  owed here; the check order you and `ethos` independently converged on is now the documented
  field recipe in the trust-aware-receive section above.
Cross-node meet / GC (S29b):: *Mechanism now built* (S78, PRD 0011, owner ruling R-6):
  `VersionVector::meet` and the roster-fixed `Stability` tracker shipped ahead of your wiring, the
  same way the reject mechanism did (justified by the crate-wide bounded-resources boundary, not a
  consumer ask). Your two boundaries survive intact as the *adoption* gate, and reading
  `merge_remote` / `synopsis` against source (2026-07-02) added a third your R11 falsifier list
  does not yet name: (a) *membership*, the roster is a constructor argument, so the tracker is
  only meaningful once you own a declared, finite member set (your local-hash shards still are not
  one); (b) *reclamation*, the watermark earns its keep only when you *act* below it (truncate,
  compact), reversing your strictly-append-only posture; and (c) *gap-freedom*, because your
  `Horizon` is a liveness high-water that runs past holes (order-free apply can hold `{0, 5}` and
  publish `6`), an *upper bound* of a cut rather than a cut, and a meet over upper bounds
  over-claims stability: reclamation below an over-claimed watermark can destroy the last fillable
  copy of an event a replica still has a hole for, a permanent gap (PRD 0011 R8). The meet needs
  genuine cuts, which for you means the R9 exact have-set (DVV) or contiguity-enforced apply;
  minerva's half of that fork now exists held-in-reserve (`DotSet`, PRD 0012, `dot = sequence + 1`
  in your basis) while your ranked first discharge stands. One
  shape note for that day: `Horizon` deliberately exposes no inner `&VersionVector` (byte-only,
  basis-asserting construction), so the natural adoption path is a pinax-side wrapper that reports
  decoded synopsis frames, keeping the tracker's whole family Horizon-basis by construction, your
  own newtype discipline extended one type up. When all three are owned, the sections are the
  `D_n` cuts you already gossip and adoption is the causal-stability section above. Alma has since
  closed the base tracker's first-contact review without a shape change; a bulk constructor would
  now need to pay rent as `pinax`-specific convenience.
  Your 2026-07-02 notes confirmed the roster shape from the consumer side: membership, when it
  arrives, will enter `pinax` as a trait seam (the `Politeuma` precedent, the register staying
  with an identity-owning consumer), and a caller-declared fixed roster as a constructor argument
  is exactly what that seam would feed; no friction anticipated on the shape, so the gates above
  are what remain.

*DVV adoption stays deferred, and that is correct; minerva's half now exists in reserve.*
Per-source FIFO has no same-source concurrency, so the
gaps Dotted Version Vectors solve cannot arise: `Event.deps` already carries the per-event dot at
`deps[sender]`, and the contiguity invariant forbids the non-contiguous holes a DVV disambiguates.
Since S83 the *delivery-side* half of your R9 fork is in-tree as `DotSet` (PRD 0012, owner ruling
R-7): the exact have-set whose `floor()` is a gap-free cut by construction (the honest stability
report), whose `high_water()` is your `Horizon` quantity, and whose `holes()` is the named fetch
list, `dot = sequence + 1` in your basis. It changes none of your decisions: your ranked first
discharge (contiguity-enforced apply, your `owner-comments.adoc` R-6) stands, and the
*production-side* sibling-tagging DVV becomes relevant only if one `station_id` ever hosts
genuinely concurrent writes (a server proxying many clients under one key, i.e. partial
replication with a resolver-side causal context); that half remains unbuilt and ungated.
Until a falsifier fires, the plain station-keyed vector is the honest record.

=== `ethos` (the deliberation layer)

*The ἔνδοξα watermark is PRD 0009's participation gate, consumed as a fold.* A Πρᾶξις becoming
κυρία once enough distinct authorized endorsers weigh in is exactly the participation watermark
PRD 0009 names, and `ethos` is the caller it names. Per R6 the participation gate *stays a caller's
gate*: you build the monotone weighted-threshold fold yourself (the `ΚανὼνΚυρώσεως::κυρῶσαι` you
have), and minerva ships only the test-only `Quorum` that proves the seam carries the shape. That is
the intended division of labor, not a missing feature.

*Your tier weighting is a legal, well-behaved gate.* Distinct-endorser dedup plus a saturating
weighted sum against a fixed threshold is monotone (the contributor set only grows), so it is an
R1-legal `Gate`. The finite tier weights (σοφοί 3x, πολλοί 1x) with a finite threshold `k` stay
Scott-continuous: PRD 0010's side condition only breaks for weights with infimum `0` or a limit
threshold, which finite tiering is not. So the fold is sound today and stays sound if you ever
promote it to a machine gate.

*You took the enactment path, and it needed nothing from us (2026-07-01).* The prediction the
2026-06-21 exchange recorded ("wrap the same fold as a `Gate` and run a `metis::Ideal` over it")
held exactly: your `ἐπικύρωσις::Ἐκκλησία` runs `Ideal<(), ΠύληΚυρώσεως>` over your own gate, and
every κυρία proposal releases once, in ledger-`Kairos` order. Two things about that gate are worth
recording as the *caller-typed-`Dep` pattern*, since the next gate author will want them:

* *The `Dep` is yours, entirely.* Since S27b, `Event<T, D>` is generic over the dependency
  descriptor and a `Gate` impl chooses both `Dep` and `Progress`. Your `Dep` is a fused enum (an
  endorsement carries the endorser's 32-byte `ΔημοσίαΚλείς` and advances a per-proposal
  distinct-endorser tally; a proposal carries the assembly's threshold rule, gates on it, and goes
  terminally stale once enacted), and your `Progress` keys itself by proposal seal. The `u32`
  sender argument simply goes unused. This is why the anticipated "keyed `Progress`" request
  (rec #2 of your 2026-06-20 disposition) was *withdrawn rather than granted*: the seam already
  carried it, and nothing was owed upstream.
* *You did not need `And<Causal, Participation>` either.* Your enactment determinism comes from
  merge-stable ledger stamps, not causal dependency completeness, so the composition stays an
  available option (the gate algebra above), not a requirement. Do still not reach for `Or` (a
  proven non-goal).

*The read discipline your testing found: drain before ranking.* Your ordinal conflict pick reads
each released proposal's κύρωσις, and the general release-at-crossing rule (the gate-algebra
section above) applies with full force: read at release, every proposal looks threshold-weight and
"rank by κῦρος" silently becomes "rank by time." Drain `ἐπιτελεῖν` to `None` first, then read each
seal's tally.

*So the deinotēs seam stays uncarved, now with a consumer on record (the corrected merge level).*
The 2026-06-21 note recorded your merge level as *fold*; building the consumer showed that
conflated the verdict with enactment. The honest ledger is three-layered: the *verdict* folds (your
revisable κυρία LWW view over the G-Set, the only genuine merge); *release* is not a merge at any
level (every κυρία proposal, once each, in `Kairos` order, the machine default); and *conflict*
among concurrent κύριαι is *ordinal*, caller policy above the release stream (rank by weighted
κῦρος, tiebreak lex-posterior by `Kairos`, which is PRD 0008's composition-with-the-kairotic note
in the wild). Your `ἦθος::νόμος` register then makes that ordinal pick a strict total order, so the layer
above the release stream is again an order-independent fold: the whole stack is fold -> once-only
release -> fold, with the non-merge layer exactly one deep. That register is the first *real*
conflict-consumer on record, and it needed *no selective release*, so the one capability a carved
`Resolver` seam would add that caller code cannot express (choose which deliverable event releases
next, against the machine's `Kairos`-minimal rule) remained gated until stokes supplied exactly
that irreversible-effect consumer. S234 now provides the narrow
`Ideal::pop_ready_event_where` operation; it does not carve the broader `Resolver` framework.

*The seal stamp: use `Kairos::to_bytes`, do not hand-roll the layout.* The byte frame is
`[0x01, physical(8), logical(2), kairotic(2), station_id(4)]` big-endian, so the *kairotic tag is
more significant than `station_id`* (that ranking is the point of the kairotic dimension). But the
constructor argument order is `Kairos::new(physical, logical, station_id, kairotic)`, with
`station_id` *before* `kairotic`. A seal that serializes fields in constructor-argument order
therefore puts them in the wrong significance order, the `station_id` / `kairotic` "swap" you saw.
The fix (your M2) is to seal over `Kairos::to_bytes()` directly, the canonical 17-byte frame, never
a hand-rolled field concatenation; `KAIROS_WIRE_LEN` is public so you can split the stamp prefix
from the rest of an envelope.

*Your shared-clock `Producer` request is answered, plus the half the swap also needed
(2026-07-01, S73).* Your `κοινωνία` addendum asked for a `Producer` over the station's shared
`Arc<Clock<TS>>`; reading `Κοινωνός` against `producer.rs` showed the promised swap needed one more
thing the addendum did not name: your receive path (`δέχεσθαι`) folds through the *bounded*
`Clock::try_observe` plus a knowledge merge, a shape `Producer::observe` (unbounded) cannot
express, and routing the check and the fold separately would tick the clock twice per receipt.
Both halves shipped (see the shared-clock section above): `Producer::new` now takes a sealed
`ClockCarrier`, and `Producer::try_observe(&event, max_forward_skew)` is your
`δέχεσθαι` fold as one all-or-nothing operation. The swap is mechanical:
`Κοινωνός { γνῶσις, ὑποδοχή }` becomes `{ παραγωγός: Producer<Arc<Clock<TS>>>, ὑποδοχή }`,
`κοινοῦν`'s three hand-rolled lines become `παραγωγός.produce(kairotic, πεπραγμένον)`, and
`δέχεσθαι`'s check-then-merge becomes `παραγωγός.try_observe(&συμβάν, ὅρια.max_forward_skew)`
before the existing `try_insert`. Your seal-verify-first ordering and your capacity policy are
untouched; keep them caller-side. This deliberately does *not* generalize `Producer` across
gates (your own rec #7): it is the causal minter you asked for, over a shared clock.

*Migration, when you adopt the metis-era minerva.* The breaking `kairos` changes are small and
localized (see "Breaking changes already landed"): `now` / `after` take the kairotic and return a
bare `Kairos` (contention moved to `Clock::stats`), and `with_default_config(time_source,
station_id)` dropped the `initial_kairotic` argument. The deinotēs resolver (your M4) is PRD 0008,
the most deliberately held menu item. Your enactment consumer remains on record needing no seam
(above); stokes subsequently fired the narrower selective-release gate, while general resolution
stays caller-side.

=== `alma` (the collaborative editor)

*The chartered consumer arrived (2026-07-07, first field adoption of the causal
store).* The multiplayer editor the R-12 through R-15 chain built ahead of is now
live consumer source: alma's `src/collab/` (decision record COLLAB-1 in its
`docs/owner-comments.adoc`) drives a raw `Dotted<Rhapsody>` pair for shared text:
one element per character, glyph payloads caller-side exactly on the R-12 boundary,
dotted delete as a born-dead marker beside the observed-remove set, run-grade insert
through `weave_run` with the rank-above-children observe discipline, and
`Composer<DotMap<u32, DotFun<_>>>`
register writes through `compose_super`/`observed` for per-participant cursors
whose payloads are *element-identity anchors*, the identity-not-offset shape this
guide's causal-store section recommends. Its movement pair remains a
`Composer<Metatheses>` and also serves as the shared event-allocation ledger (the
COLLAB-17 conversion recorded below). Exchange is pull-based
`owed_to_witnessed` / `absorb` (since S191 the text exchange carries the peer's
`woven()` recording witness beside its context, its COLLAB-3, so a steady-state
round is delta-sized instead of re-shipping the ordering skeleton; the cursor
registers are support-only and take no witness); the deterministic `TickCounter`
clock keeps orderings a pure function of write history. Its convergence proof
(`tests/collab_convergence.rs`) runs two- and three-session fleets through real
editor input.

*What this adoption pins.* The genuinely-concurrent-writes condition is now met
at a field consumer (the production-side dotted-vector charter's own gate
condition); the interleaving cure (PRD 0018) and the survivor law are
load-bearing in someone else's test suite; and the integration pin is a
*sibling-checkout path pin* by recorded owner decision (alma's
`docs/supply-chain.adoc`: alma deliberately does not build from a bare
clone until this crate releases, a vendored copy explicitly declined on
R-2's own words), so the first build environment that cannot carry the
sibling fires ruling R-2's import-shaped trigger verbatim. Since its
COLLAB-4 (2026-07-08) the *retirement half* is field-consumed too: alma
gathers each participant's applied-removals report (read off the
replica's own merged state, so it cannot over-claim), meets them through
the roster-fixed `Retirement` tracker, and condenses every session
against the typed `Retired` evidence, with glyph hygiene riding the same
round: the first genuine ack round through the machinery (the bench's
`Retired::trust` stand-in is no longer the only usage shape), and the
first field exercise of the retention face end to end.

*The membership and retention story is now field-complete (its COLLAB-5 and
COLLAB-6, 2026-07-07).* A late joiner bootstraps through the owed read
against the empty claim (this guide's own "a full state is just a delta a
fresh peer has never seen", consumed as policy: no snapshot type exists at
the consumer), join-after-condense is pinned end to end, a graceful leave
retracts the leaver's cursor register so its retention pins release, and
crash eviction is a survivor's retract of the vanished register (safe by
the survivor law: a participant evicted in error recovers by publishing).
Retention runs on a cadence (a tombstone-debt threshold) and the
long-session profile the arc rulings were waiting on has run at the
consumer (`tests/collab_profile.rs`, 1,600 rounds of two-author interior
churn, sync per round): grow-only retention held 19,440 loci over a
1,440-char living document (13.5x) with a late-joiner bootstrap of 817,331
bytes at the frame's ~42 B/locus; the cadence held 5,878 loci (4.1x) and a
247,727-byte bootstrap, reclaiming 75% of all deletions; and the remaining
4,438 tombstones (roughly 25% of everything deleted, growing linearly with
churn) are *fertile* residue live text anchors through, which no roster
round can reclaim: the uncollectable-residue shape arc 5 recorded for path
compression, now with a field rate.

*The epoch boundary is now field-consumed too (COLLAB-17 and COLLAB-18,
2026-07-17).* Alma adopted the full declaration, confirmation, adoption,
seal, stratum, shadow, and consignment cycle over a quiescent roster. At the
boundary the 4,438 fertile tombstones fall to zero on both replicas, 8,876
loci are swept, the skeleton resets to the 1,440-character living document,
and the late-joiner bootstrap falls from 820,323 to 60,547 bytes. The
translation inventory is complete: glyphs re-key through the retired map;
cursor values whose anchors were swept degrade by byte to the same order
slot; marks and byte-span undo hold no cross-generation identity today. The
retired map is therefore consumed during the fold and retained for zero
generations. Identity-anchored undo is the first named surface that would
change that policy. Alma reports that `EpochShadow::consign` held its shape
at first contact and requests no change to the refusal-hands-back seam.

*The boundary stall is a schedule you own, and its safety is now a type
(S270, ruling R-54; S272, ruling R-56).* The transition is one consuming
door since S272: `EpochStratum::into_transition()` moves the pristine
native plane and the shadow out together (`EpochTransition { native,
shadow }`, no clone: no door discards the native plane before it
reaches you, so re-deriving it is never needed; `EpochShadow::open` is
retired, the breaking change alma requested and consented to). Your native allocator basis is the
native store's own per-station high water. Replay the window traffic you
have already delivered through `EpochShadow::deliver_text_batch` /
`deliver_moves_batch` rather than the per-delta doors: validation and
refusals are identical, and the maintained decision surface refreshes
once per plane instead of once per delta, which the S270 phase split
measured as the difference between a multi-second and a sub-100 ms
boundary at cadence 20,000. The remaining term, re-merging the
generation log at the adopted cut, is schedulable, and the schedule's
safety laws live in `EpochPreparation` since S272:
`EpochPreparation::begin(&declaration, base_text, base_moves)` pins the
candidate (refusing a base already above the declared cut), each
`absorb_text` / `absorb_moves` folds one covered log entry on whatever
idle tick you own (the chunk you feed is the budget; a window entry
returns the `Window` verdict and is left for the shadow replay). Each
absorb names the entry's own event dots, your log's annotation under
the dotted-deletes half of the one-event-space duty: the events are
the classifier, because a deletion is a bare context delta whose bytes
cannot say which side of the cut it fell on, and an entry naming no
events (an undotted retraction, invisible to every cut) conservatively
stays window-side; structural context-and-skeleton scans back the
declared events as defense against mislabeled entries. Then
`finish(&adopted)` proves the exact winner-and-cut match before running
the full stratum validation, with the losing-candidate and drifted-cut
misses typed (`EpochPreparationMiss`) so the full-rebuild fallback is
demanded, never remembered. One obligation stays yours under staging
and rebuild alike, stated on the type: the proof is structural
(coverage, bounds, foldability), not a content audit, so feeding the
generation's actual covered log is your log discipline; a base whose
context covers the cut but whose store is hollow re-founds hollow on
either path. If you declared, your dry-run already built
the pairs: `begin` from them and absorb only what arrives after,
instead of paying the rebuild twice, and carry forward as window
traffic the entries your dry-run skipped as above-cut (they were
classified by the same event-dot rule; the shadow replay owns them at
adoption). Which candidate to stage in a contested window, and
whether to stage several, stays your policy. The reference recipe is the
retention arm of `bench/src/bin/longevity_epochs.rs` (its staging now
wraps this type; what remains caller-side there is exactly the schedule
and the timers); measured numbers are in the closing report's S270
addendum.

*One event space per station is an adoption precondition, not a style
preference.* Epoch declarations, both adopted document planes, the sealed
join, the frozen map, and the consigned context must range over one dot space
per station. Independent self-allocating `Composer` instances for text and
movement can mint the same `(station, counter)` in different planes; their
images then collide at re-foundation. Alma found this while converting its
former three-facade shape. Its repair uses the movement pair's context as the
allocation ledger: every weave run, delete marker, movement testimony, and
declaration reserves there first, so the union context is a gap-free prefix
and `Cut::floor_of` is the honest witnessed report. `Composer` itself remains
a one-pair facade and does not hide shared mutable allocation. A future
ergonomic surface, if a caller needs to leave the raw pair, should be an
explicit station-scoped ledger or reservation capability above composers,
with durability and reporting visible at that owner boundary.

*The bootstrap bytes now have their codec (S197, PRD 0023, ruling R-25).*
The numbers above fired arc 5's snapshot half:
`Rhapsody::snapshot_encoded_len` / `to_snapshot_bytes` /
`from_snapshot_bytes` is the
run-compressed sibling of the canonical frame, 6.3x smaller on honest
two-author traffic at 65k chars (~42 B/locus down to under 7), decode
proven total (the second dual-homed Creusot core) and fuzzed. The length
preflight is allocation-free and uses the codec's own chain law, so an
embedding can reject an over-budget frame before serialization. When alma
grows a transport or a persistence checkpoint, the whole-store form is
this frame, not the v2 frame; the owed read stays the steady-state
exchange (a snapshot is for joining and saving, never for gossip), and
the first *embedding* of the frame in a protocol or a stored artifact
fires ruling R-8's pin obligations, so file it when it happens.

*What its flat-text surface now asks, kept precise.* Alma is still not a
`Node` consumer, and the structured pages/blocks surface remains the named
prospective ask for the subtree half of stage C. But both structured-stage
signals now have flat-sequence consumer evidence on the record
(2026-07-07): its COLLAB-8 pins the stage C hazard in the field (its
`:move` is a hull replace, so a concurrent edit converges *stranded* at
the old identity's order slot; the exhibit test is written to fail the day
moves preserve identity), and its COLLAB-7 hand-rolls stage D's point half
caller-side (marks captured as identity anchors across an absorb, the
cursor-register recipe applied to marks) while filing the range asks
(shared marks, proposal spans, comments: two sided ends whose overlap and
convergence are a store's problem). *That filed ask fired stage D* (S195,
ruling R-23, PRD 0021): the range half now ships as the `Verge` boundary
vocabulary, the `Rhapsody::extent` projection (verdicts total: covered,
empty, inverted, dangling; a deleted boundary element still bounds through
its tombstone slot, the degrade its own recipe proved), and the
`Scholia<T>` mark store (tags opaque; sharing and editing are the register
recipes; `anchor_dots()` is the pin set its retention cadence subtracts
before minting claims). Expansion at a span's edge is the side choice at
mint (After-start / Before-end expand; Before-start / After-end do not),
so its selection, comment, and proposal-span models choose behavior by
anchoring, and no schema entered the crate. Adoption is its call through
its own gates.

*The reorder half of stage C fired on the COLLAB-8 defect the same way*
(S196, ruling R-24, PRD 0022): movement now ships as testimony
(`Metathesis` / `Metatheses`, in its own causal pair beside the text's)
plus the `Rhapsody::recension` replay, so a moved element keeps its identity
and its stranded-edit exhibit flips (a concurrent edit anchored to the moved
element follows it, never stranding), with concurrent-move cycles refused
deterministically and surfaced in `refused()`. Undo is a `retract` of the
testimony's dot. The *subtree* half (blocks reorganizing a structured page)
stays the named ask on the pages/blocks surface alma does not yet run.

*The adoption blocker fired next (S203, ruling R-30, PRD 0022 R7):* a
per-keystroke editor that adopts moves must read the document through a
`Recension` on every event, and the eager replay at 64k chars
(~26.8 ms) sat four orders of magnitude above alma's every other
per-event term, so the cure for COLLAB-8 was unadoptable in practice.
`Recension::collate` is the door: hold one `Recension` beside the pair,
call `collate(text, moves)` after every compose, absorb, retract, or
retirement round, and read through it; the call costs the changed
regions' replay plus per-call possession scans (since S205, ruling
R-32: one `O(pages)` word-comparison walk over the visible occupancy planes plus flipped-dot extraction (the visible scan's cost no longer depends on its have-set fragmentation), beside the woven have-set diff (floor-compact on honest traffic) and the surviving-testimony record scan;
~1.4-4.9 us per keystroke under a live record on the bench fixture,
~0.3-3.4 us caught-up, ~8-17 us per block-shaped move event) and is
always exactly equal to a fresh `recension()`.

*The owed number ran the same day (alma COLLAB-9, 2026-07-10), the
adoption landed, and the instrument bit twice.* Alma's `:move` now rides
the machine end to end (announced relocation intent paired at its owner
core, the two-phase root-and-straggler recipe read against the evolving
collation, every relocation normalized to its upward complement so a
typed document's downward destination never sits inside the mover's own
subtree, testimony hygiene in its retirement round) and the COLLAB-8
exhibit flipped: a concurrent edit follows the moved line through real
editor input. The measured per-event profile (release, replica layer, a
2 KB document under interior churn: 512 keystrokes, 124 line moves, 447
surviving testimonies, ~500 fertile interior tombstones): keystroke
~137 us, line move ~83 ms, absorb ~6.9 ms per round. Two seams now have
field numbers where the bench fixtures had none:

* *The collation's possession scans cost their representation, not the
  change* (~135 us per keystroke with an EMPTY movement record, so the
  term is the mark diffs, not the move machinery; per-diff attribution
  between the woven and visible scans is still owed, both riding the
  same `DotSet::difference` mechanism): each scan pays a tree lookup
  per station and a tree-membership probe per retained EXCEPTION (the
  have-set entries above the floors) beside enumerating the actual
  differences, a churned document's visible have-set is nearly all
  exceptions above a low floor, fertile interior residue cannot be
  retired away, and the `collate_typing` fixture never deletes, so the
  term was invisible upstream. This is the collation's next number,
  unnamed until now: the cure direction is a possession pass costing
  the pair's actual difference, its own slice against this profile as
  the instrument (with the per-diff isolation its first measurement).
  *Cured the same day (S205, ruling R-32)*: the visibility diff is now
  one paged occupancy walk (a 64-bit mask per station and 64-dot page,
  word compares plus the flipped dots, exact for any pair), and the
  new `collate_churn` instrument, built to your COLLAB-9 shape, fell
  from ~57 us / ~2.6 ms per churned keystroke at 4k/64k chars to
  ~2.5 / ~4.9 us, parity with an undeleted document. Alma's S225
  re-profile confirms the cure at field shape: the empty-movement control
  fell from COLLAB-9's roughly 135 us to 8.38 us per keystroke, with a
  465 us absorb.
* *The fragment-splice residual (R-30's named next cure) has its field
  number*: ~83 ms per line move on a 2 KB typed chain, the chain-shaped
  region re-placed at slot granularity once per testimony. *Cured the
  same day (S204, ruling R-31)*: the placed-to-placed re-placement now
  splices the region through the order thread as whole fragments, so a
  chain-shaped region costs its runs, never its characters. The
  `collate_move_suffix` instrument fell from ~642 us / ~3.45 ms /
  ~14.4 ms to ~3.5 / ~14.4 / ~46.8 us across 4k/16k/64k (183x to 307x,
  a same-session stash A/B on one machine state), with typing, the
  fresh move, the idle call, and the eager replay unchanged within
  noise. Alma's S225 re-profile confirms a material but incomplete field
  improvement: the line move fell from roughly 83 ms to 46.66 ms. The
  operation remains frame-breaking because alma expresses one line move
  as a root testimony followed by boundary-straggler testimonies, each
  collated against the intermediate reading.

*The predicted record-scan sequel did not fire (S225, ruling R-43).*
With 447 surviving testimonies the live arm measured 11.52 us per
keystroke, 46.66 ms per line move, and 3.91 ms per absorb. Two internal
prototypes tested the roadmap's next-cure hypothesis directly: exact
copy-on-write testimony pages removed the recension's whole-record diff,
then a maintained target index also removed `moves_of(target)`'s scan.
The line move stayed at 47.35 ms and 48.04 ms in the same release window,
so neither prototype shipped. The dominant remaining term is repeated
region replay across alma's multi-testimony recipe, not record lookup. A
batch movement/replay door now needs a joint semantic design: later
stragglers choose anchors from intermediate readings, so merely deferring
`collate` would change placement. The 1,600-round retention profile
reproduced its earlier counts and bytes exactly, leaving the memory and
epoch premises unchanged.

*That door shipped as S226 (ruling R-44).* Call
`Recension::with_movement_batch(text, &mut moves, |batch| ...)` only for a
local, strictly forward movement recipe over a fully placed reading. Minerva
retains ownership of the scope while the callback borrows a capability over
the concrete `Composer<Metatheses>` beside the recension and
exposes only `locus`, `children_of`, `anchor_for_visual_insert`, and
`move_to`. Both the canonical text topology and the current effective reading
must be fully placed; a move-repaired dangling birth stays on `collate`, since
superseding its repair could expose the dangling region again. Every accepted
testimony updates the effective topology before the
next placement read; tidy re-moves unwind the superseded play-log suffix first,
because alma exhibited a move that looks cyclic until its prior testimony is
removed. The positional thread is materialized explicitly before the owner
returns, with `Drop` only the unwind backstop, and full order/offset reads are
unavailable while it is deferred. Typed refusals occur before record mutation; delayed,
dangling, nonmonotonic, or station-exhausted work stays on total `collate`. The clean alma
prototype reduced the 64-line churn arm from 50.47 ms to 32.68 ms per line
move and passed its structural and convergence suites. Alma adopted the
boundary as COLLAB-11 (`89f0895d`); three release runs on the live consumer
measured 17.94 to 18.08 ms per line move, 12.01 to 12.45 us per keystroke, and
4.11 to 4.18
ms per absorb with the same 447 surviving testimonies. Alma observes every
received movement rank before merging, maintaining the batch's global-rank
precondition even when the next destination bucket has a lower local maximum.
The remaining move cost still needs phase attribution before a positional
planner is justified. Do not apply the batch mechanically: the clean
two-testimony chain control is slower at 596 us versus 198 us sequential.

For first-party attribution, enable `timing` and use
`MovementBatch::move_to_profiled`. Its `ProfiledMovement` carries the ordinary
witness and causal delta beside causal-record composition and a structured
topology profile: one outer duration, disjoint play-search, suffix-unwind,
suffix-edit, and suffix-replay durations, and exact search, suffix, cycle-walk,
refusal, and locus-replacement counts. Cycle validation is nested under suffix
replay with its own duration and exact exclusive guard classes, bounded-walk
nodes, and terminals. The capability implies `std`; bounded
structural counts remain available through the separate `instrumentation`
feature without changing the core's `no_std` floor.

The structured `topology` field replaces the provisional flat
`topology_placement` duration introduced one slice earlier. The old field is a
deprecated compatibility alias populated from `topology.total`; new code
should not depend on it.

The cycle refinement intentionally breaks the unreleased flat work fields
rather than retain two mutable-looking sources of truth. Migrate
`topology.work.cycle_guards_skipped` to
`topology.cycle_validation.work.guards_skipped`;
`cycle_guards_entered` to the sum of `self_guard_entries`,
`live_guard_entries`, and `stale_guard_entries`; `cycle_chain_nodes_visited` to
`chain_nodes_visited`; and `cycle_refusals` to `refusals`. The remaining
topology-work fields keep their paths.

The allocation-free follow-on removes the provisional
`repeated_node_terminals`, `visited_sets_created`,
`visited_set_insert_attempts`, and `visited_set_insertions` fields.
`anchor_cycle_terminals` retains the repeated-node terminal's meaning without
exposing the discarded set implementation. A nonzero count means an entered
walk ended in a legal non-target cycle of unplaced birth anchors; it does not
refuse the play. `invariant_refusals` counts only exhaustion of the larger
defensive bound after allocation-free Brent detection has had enough steps to
terminate on any finite skeleton. Such a play refuses fail closed.

On Alma's 64-line field profile, the topology structure isolates suffix replay
as the residual: 10.18 to 10.30 ms of 10.30 to 10.43 ms topology application,
about 668 replayed plays and 210,049 cycle-chain nodes per move. Search and
suffix editing are negligible. Nearly every replay enters the monotone cycle
guard, so the next experiment belongs to validation rather than a target index
or positional planner.

That validation experiment finds the guard mostly live, not stale. Three
unchanged release runs classify 598.3 live-child and only 27.9 stale-monotone
entries per move, beside 40.5 self entries and 1.2 skips. Cycle validation costs
10.71 to 10.90 ms and 210,049 visited nodes produce 209,939 distinct fresh-set
insertions. Pruning stale guard membership cannot remove the dominant work.

The allocation-free bounded walk validates that representation. Three stable
release runs preserve the exact workload shape and report no non-target anchor
cycles or invariant refusals while cycle validation falls to 5.020 to 5.113
ms, topology application to 5.294 to 5.418 ms, and the whole line move to
11.709 to 11.940 ms. The repeated two-testimony control measures 165.54 us
sequential and 575.07 us batched, so the explicit batching policy remains
necessary. Cycle validation and the consumer's 4.911 to 4.953 ms
straggler-tail ancestry walk are now co-dominant. A shared exact ancestry
capability is justified, and S228 (ruling R-46) settles its contract without
selecting a representation. It is an inclusive relation over a fully placed
effective reading: `Same`, `Descendant`, or `NotDescendant`, with unknown dots,
unplaced state, and invariant exhaustion kept as typed failures. `Recension`
owns the derived coordinate; a movement batch temporarily holds that same
coordinate and updates it atomically with every reverse-unwind and
forward-replay locus transition. Reverse unwind restores logged loci without a
cycle query and may privately enter `Unplaced` for a historically unknown
anchor; bounded fallback and incremental repair must restore `Ready` before
`move_to` returns. If changed suffix verdicts leave the tentative result
unplaced, Minerva restores the exact pre-call topology and ancestry state and
all per-call batch bookkeeping, including `anchored`, `highest_rank`, and the
changed flag, then returns `MovementBatchError::UnplacedResult` before
composing a testimony.
The error carries the requested target and first unplaced dot; earlier
successful calls in the batch remain committed.
Forward origin moves bypass the dot relation and link the synthetic root. The
coordinate may not be cloned or rebuilt per batch, and queries or admitted
batch updates may not allocate coordinate storage. Replay fails closed by
refusing an indeterminate play; a consumer must abort before composing its
first testimony. See xref:metis-exact-ancestry.adoc[the exact ancestry
capability]. Total collation keeps the bounded parent-walk fallback for lawful
unknown or unplaced destination anchors, preserving the unborn non-refusal and
dangling-arrival re-evaluation; capability unavailability cannot change those
movement verdicts. Only an invariant failure after both operands are known and
placed refuses fail closed. An index, topology overlay, or lower-plane reuse
still needs a measured concrete comparison before production code changes.
Batch admission returns `MovementBatchError::AncestryUnavailable` for
coordinate capacity or validation failure without invoking the callback;
`UnplacedReading` remains the fully-placed topology gate.

S229 (ruling R-47) selects the private representation but ships no API.
A detached rooted link-cut spike put one recension-owned coordinate on Alma's
consumer and replay query paths. The unchanged field arm fell to 2.085 to
2.133 ms per line move; the tiny sequential and batched controls fell to 25.48
to 37.51 us and 422.75 to 430.73 us. The spike's seven delayed/unplaced
failures are the implementation gate, not accepted behavior: Minerva must ship
R-46's parked state, repair, rollback, typed errors, and reference-walk laws
before Alma may adopt the capability. See
xref:metis-exact-ancestry-representation.adoc[the representation decision].
S230 (ruling R-48) implements the private compact-addressed node arena,
transactional state construction, bounded link-cut core, and reference-walk
oracle. S231 (ruling R-49) activates it under sole `Recension` ownership.
`Recension::with_exact_ancestry` and `MovementBatch::relation` lend only a
borrow-scoped exact query; admission and queries retain separate typed failures.
Every effective-locus transition now synchronizes the coordinate, batches
transfer it without clone or rebuild, and tentative unplaced suffixes roll back
before record composition. S232 completes Alma's adoption: straggler-tail
discovery borrows `ExactAncestry` once across all candidates, release builds
perform no parent walk, and debug builds retain the old walk only as an
independent oracle. The intentional instrumentation migration reports exact
query counts instead of parent-chain visits.

The residuals alma recorded on its side: undo of a move replays the
reverse hull as a plain edit and re-forks (the retract-shaped cure
joins its identity-anchored undo ask), and the end-of-buffer
trailing-newline shapes are not element rotations and keep the plain
capture.

Its transport existed only in-process when this paragraph was first
written. Its TRANSPORT-1/TRANSPORT-2 slice (S271, ruling R-55) was
audited in its *uncommitted* worktree: alma transport v1 there carries
the rhapsody v2 frame, the have-set frame, and the kairos stamp beneath
an authenticated envelope (the registry section above records the
observed shape under the Polis S3 waiting rule), and spells its
movement section as `(testimony dot, target dot, sided anchor, kairos
rank)` per testimony. The movement-record codec activated on alma's
same-day activation request (S272, ruling R-56, refining R-55's timing
gate): the canonical `Metatheses` frame ships now (the registry section
above records the shape and what still waits), the layout being
minerva's own store spelling rather than a copy of alma's bytes, while
the R-8 registry pin of alma's embedding, the coordinated-bump duty,
and their duplicate parser's retirement wait on their landed commit
exactly as before. The generation-qualified address frame family's
"consumer that serializes dots" trigger remains treated as observed,
not met, with the frame charter deliberately held for the coordinated
design session where alma brings its inventory (their event inventory
arrived with the same letter; the charter still waits for the landed
evidence commit and the versioned table it promised). The arc 10 number the profile was named
for was measured and cured (S191, ruling R-21, adopted the same session);
arc 11's phase-one ruling has its consumer profile beside the S192
instrument numbers.

*The positional reads its viewport math was priced against now exist*
(S200, ruling R-27, additive): `Rhapsody::order_len` (`O(1)`),
`order_at(offset)` / `offset_of(station, dot)` (the offset coordinate in
both directions, `O(log)`, tombstone slots included with the extent
read's degrade), and `order_walk_rev` / `order_walk_rev_before` (the
scroll-up window), all delegated by `Recension` for a moved reading. An
editor that renders by offset or does caret arithmetic no longer pays an
`order()` pass per question (~4.5 ms at 65k chars; the reads measure
nanoseconds, flat). Adoption is its call through its own gates.

*The paste door now exists* (S211, ruling R-33, additive): a paste,
import, or file-open stops being ten thousand individual weaves. Reserve
the run's ranks in one clock commitment (`clock.now_run(len, tag)`),
weave it in one call (`Rhapsody::weave_run(first, anchor, run)` (the reservation is consumed: affine, spent at most once),
all-or-nothing, through your existing `Composer::compose` closure exactly
like a keystroke), and ship the one delta; the receiving fold absorbs a
run-shaped delta at run grade on its own, with no adoption step on the
receive side. On the recorded window a 100,000-character paste fell from
~116 ms to ~4.1 ms locally (fresh document; ~12.5 ms into the 64k churned
fixture) and its absorb from ~37.5 ms to ~2.8 ms (fresh; ~5.4 ms
churned), every arm inside the 16 ms frame budget. The members chain by
the clock fold's own successor rule, so a pasted run coalesces in the
identity plane, the order thread, and the snapshot codec exactly as
patient typing would (byte-identical snapshot bytes, pinned). The paste
exchange's fixed removal-context term on a churned document (the
have-set carrying every prior removal, ~524 KB on the S210 fixture) is
deliberately untouched: that is the epoch/retention track's territory,
not the ingest door's.

=== `polis` (the replicated coordination ledger)

*The committed core is a causal fact store, not a delivery queue.* At Polis
`45b17aa`, one immutable coordination fact occupies one dot in
`Dotted<DotMap<ThreadId, DotFun<Fact>>>`, and `Composer` is the sole local
assignment path. Thread state is a total caller-owned projection over partial
receipt. Concurrent dispositions remain concurrent facts; Minerva merges them
and Polis derives disagreement without asking timestamp order to decide it.

*Keep same-dot disagreement ahead of total merge.* `DotFun` keeps self when two
copies carry one dot because its basis invariant says honest copies carry equal
content. Polis owns the boundary that can falsify that assumption: authenticate
the station, reject duplicate candidate dots, compare the whole candidate batch
with committed content, and only then construct a covered delta. Do not make the
causal-store lattice fallible to absorb equivocation policy.

*Exact repair is adopted.* Polis S2 (`45b17aa`) derives possession from the
causal context, serves `local.difference(peer).take(...)`, pages `holes_for`,
reports `hole_count`, and preflights a bounded candidate context before one merge. Its fact-count,
payload-byte, station, hole, exception, and forward-gap limits remain Polis
policy. In particular, a one-dot bounded insert would not protect the actual
batch merge through `Composer::absorb`; keep the clone-and-check path until a
landed profile proves a reusable Minerva mechanism is needed.

*The have-set frame has its first landed protocol embedding.* Polis S3
(`5d10141`) places
`DotSet` v1 bytes inside an authenticated, allocation-bounded possession
envelope. Its `max_exceptions` limit is exactly the expansion ceiling Minerva
needs before materializing wire runs, so S242 exposes `HaveSetDecodeBudget` and
the explicit-budget exact and prefix decoders. Pass the validated exception
limit into `Possession::from_wire_bytes`; only then apply Polis's station, hole,
and forward-gap checks. The v1 bytes and unqualified `from_bytes` secure default
do not change. Ruling R-8 now pins the inner v1 bytes to Polis possession
protocol v1. Minerva's property and fuzz target cover the exact-slice,
explicit-budget seam; Polis separately covers the complete authenticated frame.

Persistence, authentication, payload framing, retry, station leasing, and
repository authority stay in Polis. `Stability` waits for its later fixed-roster
deployment, durable application-delivery reports, and an actual retention
operation. A path dependency that still resolves against `../minerva` is not the
0.2.0 release trigger; a clean environment that cannot provide the sibling is.

== The caller-gated menu

These are designed and deliberately unbuilt. They are not a roadmap we will deliver on our own
schedule; they are options you can pull. The "what we need" column is the boundary we will not
guess at. Pulling one means: tell us the use case, and where the listed decision lives in your
crate.

[cols="1,2,3,2",options="header"]
|===
| Item | What it gives you | What we need from you | Spec

| Skew clamp policy | The _policy_ half of the trust-aware receive above: an *advance-but-cap*
  variant beside `try_observe`'s reject, for a deployment that prefers to admit a too-far-future
  stamp with bounded effect rather than drop it. The reject mechanism shipped in S24.
| *`plethos` / `pinax` declined clamp* (ADR-0012: it fights pinax's clock-dominates-stream invariant),
  so this now waits on a *different* consumer that wants advance-but-cap, and on its clamp semantics
  (cap the clock advance only, or also rewrite the admitted stamp). We will not pick the tradeoff for
  you; compose your policy on `try_observe`'s `SkewExceeded` until one does.
| PRD 0008 (adversarial reading)

| Stability roster membership change | The _policy_ half of the causal-stability watermark above:
  join / leave / crash semantics over a live `Stability` roster (regress-on-join, un-pin-on-leave,
  epoch'd trackers), or a richer cross-node type (a CRDT-of-trackers that gossips itself, a
  bulk-report constructor). The meet mechanism shipped in S78.
| A caller's membership model: who declares the family, and what a joiner or leaver means for
  state already reclaimed below the old watermark. We will not invent membership semantics; build
  a new tracker per roster epoch until one exists.
| PRD 0011 (open questions)

| Frontier resolution (_deinotēs_) | The narrow selective-release mechanism now ships as
  `pop_ready_event_where`: filter the live ready set while `Ideal` retains readiness and progress
  ownership. The broader typed seam for what a concurrent frontier _means_ (keep siblings, pick a
  winner, or fold a join) remains caller-side.
| The resolution algebra and where converged state lives: an open `Resolver<T>` trait or a
  closed `Merge<T>` enum, per-key or per-frontier. The most deliberately held item: we will not
  carve this without a caller drawing it, and the first real conflict-consumer (`ethos`'s
  enactment stack: its own gate with a caller-typed `Dep` and per-proposal `Progress`, its ordinal
  conflict pick a strict total order folded *above* the release stream) is now on record needing
  no seam; its anticipated combined request was withdrawn. The narrow signal later fired when
  stokes needed to skip releases ahead of irreversible external effects. That caller drew only
  frontier filtering, so no `Resolver<T>` framework or merge catalog followed it.
| PRD 0008

| `Producer` / codec under a generic gate | `metis`-blessed minting and framing for a
  non-causal gate (e.g. `Fifo`): produce `Event<P, u64>` and frame it, the send-side mirror of
  the generic `Ideal`.
| A scalar (FIFO) caller that wants minting from `metis` rather than rolling a per-source
  counter itself (which is trivial, hence deferred).
| PRD 0009 R7

| Unified `Station` endpoint | One type owning a `Producer` + an `Ideal`, with the wiring done
  for you, instead of composing the two materials by hand.
| A caller that wants the bundle. We default to keeping them orthogonal (you wire send and
  receive), so this is convenience, not capability.
| PRD 0006 (non-goal)

| Dependency index | An index over `pending` to beat the linear `O(pending)` scan in the
  buffer.
| A real workload where that scan is the bottleneck. Pure optimization; we will not add the
  complexity speculatively.
| PRD 0005 (open question)

| Bounded `DotSet` admission follow-through | The canonical run-length
  wire frame (`to_bytes` / `from_bytes` / `from_prefix`) and the per-station repair reads
  (`holes_for(station)`, `hole_count()`) shipped as PRD 0016 (S98) and are in the stable surface
  above. Polis S2 shows why the predicted bounded one-dot insertion is not the
  actual boundary: a whole fact batch must be preflighted before causal-store
  merge. S242 separately closes the dense-frame decode question with the typed
  `HaveSetDecodeBudget`; it is not an insertion-capacity mechanism.
| Polis S2 (`45b17aa`) fires bounded reorder exposure and adopts the repair reads. Its whole-batch
  cloned-context preflight is correct and bounded; a direct one-dot insertion limit would not guard
  `Composer::absorb`. S84 is therefore re-pointed to a landed measurement showing the clone needs
  a reusable merge-side mechanism. Polis S3 (`5d10141`) supplies the landed
  frame call and exact decode-budget shape; S243 records its R-8 pin and
  composed coverage.
| PRD 0016 (open questions)

| Epoch adoption (the consumer-payload half) | The retention exit whole: the epoch rounds
  (`Epochs`, declaration through seal over your `Stability` reports), the sealed-stratum shadow
  judging in-flight old traffic, and since S263 (ruling R-50) the *seal consignment*:
  `EpochShadow::consign`, given the seal witness alone, checks the sealed join is covered
  exactly (typed `Incomplete` / `BeyondSeal` refusals that hand the shadow back for the cure,
  never a silent drop) and hands you the next generation's opening base pair with the two
  document planes carried across the boundary whole (window births, moves, and deletes at their
  judged places, floors born gap-free) and the retired map riding along as your translation
  seam. One duty rides the carry: defer native movement testimonies to the seal (the consignment
  re-spells the topology movement acceptance depends on, so a window-time verdict can flip; the
  window is bounded at two watermark rounds). The
  document planes cross on their own; what remains is yours: re-translating external references
  (cursors, held mark identities, content hashes) through the retired map, the
  generation-qualified address frame (a coordinated R-8 event), the map's retention window, and
  the marker-conversion inventory (text deletes and future undo retracts become marker-dot
  mints; the fleet replica, `src/metis/tests/fleet/`, is the executable recipe). One further
  conversion is load-bearing: every plane and protocol event adopted together must draw from
  one event space per station. Separate self-allocating `Composer` facades collide counters
  across planes and make the affine image unsound. Keep one explicit allocation ledger, reserve
  every weave, delete marker, movement testimony, and declaration through it, and report the
  floor of the adopted planes' union. Alma COLLAB-17 uses the movement pair's context as that
  ledger; minerva does not yet ship a multi-plane composer facade. Since S264
  (ruling R-51) the recipe also carries the crash-restart duties: fence your durable store
  before anything you emit (your mints and your acknowledgements are the hard points; a
  re-derived allocator that forgot an emitted dot forks identity forever), report only your
  durable floor (the tracker's join masks a post-restart regression by design), checkpoint the
  consigned base, the sealed lineage, and a clock high-water at the seal (the re-foundation
  re-bases ranks, so the sealed window's spoken stamps survive in no plane you could walk), and
  recover by replaying your journal through your ordinary delivery path, observing every
  journal-carried stamp before you mint again, plus bidirectional anti-entropy that re-serves
  under original stamps only (re-serve what your journal says you have said, not only pull what
  you missed: a crash can land between the fence and the send, and a freshly minted repair
  stamp would itself be spoken-but-unfenced), never by a second code path. One honesty note: the fleet's journal is an in-memory
  *model* of those duties (what must be durable before which emission), not a shipped
  persistence surface; spelling your durable store's outer bytes stays yours. What minerva
  ships since S273 (ruling R-57) is the lifecycle machine's own checkpoint: `Epochs::snapshot()`
  captures the complete record (generation, roster, horizon, the open window whole with its
  candidates, both report rounds, the winner latch and adoption bit, and the retained
  recognizer lineage) as an inert `EpochLedgerSnapshot` with a canonical frame (`to_bytes` /
  `from_bytes` under a caller-owned `EpochLedgerDecodeBudget`, golden fixture, proptests, and
  the `epoch_ledger_from_bytes` fuzz target). The trust seam is structural: decoded bytes are
  never a machine, an `Adopted` or a `SealedEpoch`; `Epochs::rehydrate(&snapshot, roster,
  horizon)` is the one explicit local-trust decision and refuses a checkpoint that does not
  match your current configuration, in-flight rounds ride whole so a clean restart is exact,
  and the affine adoption witness is re-earned by calling `adopt` against live stability
  evidence, never deserialized. Peer bytes stay an inert snapshot you may compare or retain;
  never rehydrate from an ordinary peer. Wrap the frame in your own authenticated checkpoint
  record (that embedding is this row's coordinated R-8 event); atomic persistence, fencing,
  digest policy, and recovery orchestration stay yours.
| Field-consumed by alma COLLAB-17/18. A further adopter should bring the inventory of references
  it holds across generations, how long it needs the retired map answerable, and which one
  station-scoped ledger allocates every adopted-plane and protocol dot.
| PRD 0024 (stage four)
|===

== Hazards to design around now

Sharp edges in the stable surface. None is a bug; each is a boundary the crate deliberately
does not police, so you must.

. *Unbounded forward skew on `Clock::observe` under untrusted stamps.* Minerva tolerates and
  reports clock skew; it does not correct it and does not synchronize physical clocks. For
  honest peers this is benign. Once your transport admits _untrusted_ stamps, a far-future
  remote stamp passed to `observe` (directly, or through `Producer::observe`) drags your clock
  forward with no bound. The defense now ships at both levels:
  `Clock::try_observe(remote, max_forward_skew)` rejects a stamp past your bound (see the
  trust-aware-receive section), and `Producer::try_observe(&event, max_forward_skew)` is the
  same bound over the producer's whole receipt (stamp *and* knowledge, all-or-nothing; see the
  shared-clock section). Authenticate the remote's `station_id` first, then `try_observe`
  instead of `observe` at your trust boundary; a skew bound on an unauthenticated peer is
  theater (it rotates identities). As of 2026-07-02 this is deployed practice at every ingress
  that exists, from both directions over the one `station_id` space: `ethos`'s `Πολίτευμα` binds
  key-to-station at the deed feed, `pinax`'s `Politeuma` seam binds station-to-key at the ledger
  ingress. See the field recipe in the trust-aware-receive section.

. *A `VersionVector`'s counter meaning is yours, and two meanings do not mix.* The type is a
  per-`station_id` `u64` count lattice; what a count _means_ is caller-defined. `metis` itself
  already reads it two ways (a `Producer`'s delivered-knowledge versus an `Ideal`'s
  released-count). Vectors carrying different meanings over the same `station_id` space compare
  and merge structurally but are *not* interchangeable. If you run both a `Producer` and your
  own consumer (a replication sequence watermark, say), hold two distinct vectors and never
  feed one where the other is expected.

. *Index basis: 1-based dot versus 0-based sequence.* `metis` is 1-based: an event's dot is
  `deps.get(sender) >= 1`, so "next owed from `s`" is the _strict_ `dot > D.get(s)`. A consumer
  keying on a 0-based sequence asks the _non-strict_ `sequence >= watermark.get(s)`; the
  translation is `sequence == dot - 1`. Crossing the two bases is a silent off-by-one the type
  cannot catch (a 1-based dot tested with `>=` re-delivers the boundary event; a 0-based
  sequence tested with `>` skips it). If you mix `metis`-minted vectors with your own, make the
  basis unrepresentable-to-confuse: a `Watermark(VersionVector)` newtype, or `next_sequence` /
  `count_seen` accessors, not a bare `get`.

. *The `Kairos` constructor argument order is not the wire significance order.* `Kairos::new` takes
  `(physical, logical, station_id, kairotic)`, but the stamp ranks `kairotic` *above* `station_id`
  (the kairotic dimension is more significant), so the canonical frame is
  `[0x01, physical(8), logical(2), kairotic(2), station_id(4)]`. Hand-rolling a stamp's bytes from
  the constructor argument order silently swaps the `kairotic` and `station_id` fields, breaking
  both the byte layout and the sort. Never reconstruct the layout yourself: encode with
  `Kairos::to_bytes()` and split with `KAIROS_WIRE_LEN`. (This is the seal divergence `ethos` hit;
  see per-consumer integration above.)

. *The knowledge join trusts the claim: an event's `deps` are an assertion, not an observation.*
  `Producer::observe` folds a received event's dependency vector into observed-knowledge by an
  unbounded monotone join (`knowledge.merge(&event.deps)`), and `Producer::try_observe` bounds
  only the *stamp's* physical skew, not the vector. So one accepted event whose `deps` claim a
  counter far past anything that exists (an authenticated-but-compromised peer can sign that lie;
  hazard 1's identity defense does not reach it) poisons your knowledge *permanently*, since a
  join cannot be undone, and every event you mint afterwards carries the poisoned entry in its
  `deps`: at every correct causal gate your own output now waits on an event that will never
  exist, neither deliverable nor stale, held until capacity. This is the vector half of hazard 1,
  and unlike the stamp there is no bound to ask for: a physical reading gives forward skew a local
  reference point, but a dependency counter has no local ground truth, so any numeric bound would
  be theater with a type signature. The defense is structural, a composition discipline. On a
  *trusted* mesh, receipt-level `observe` keeps its order-robustness (feed arrivals in any order;
  knowledge only grows). At an *untrusted* boundary, feed the producer from the `Ideal`'s releases
  instead of raw receipt: `pop_ready_event` then `observe(released.event())`. A poisoned-deps event never
  becomes deliverable, so it never reaches your knowledge, and delivery-fed knowledge is exactly
  the Birman-Schiper-Stephenson send rule (your minted `deps` reference only what you actually
  applied, where receipt-level knowledge *overstates* your causal past by everything received but
  not yet applied, which is safe but delays your events' deliverability at every receiver).
  Distrusting the lying peer itself stays the adversarial deinotēs reading (PRD 0008), caller
  policy over minerva's reads.

== Requesting a change

The posture is deliberate: we build ahead of a caller only for cross-cutting production
invariants the whole crate already honors. The bounded `try_insert`, the skew-bounded
`try_observe`, and the `Stability` watermark are the three instances, each justified by the
bounded-resources capability boundary rather than any single consumer, and each shipped in the
same four-part shape (mechanism, typed refusal, caller policy, observability read; the target
architecture names it the resilience pattern). The test we apply is recorded as ruling R-6: the
boundary must already own the concern *and* the hand-rolled alternative must fail unrecoverably,
not merely redundantly. Everything that encodes _application_ semantics waits for you.

To pull a menu item, open the conversation with: the use case, the specific decision from the
"what we need" column and where it lives in your crate, and whether you need it as a stable API
or just a seam to prototype against. That is enough to un-gate the design and schedule the
slice. Breaking changes to accommodate you are on the table while there is no other external
consumer; we will call them out here when they land.

== Where the authoritative detail lives

[cols="2,3",options="header"]
|===
| Document | For

| xref:../DOCS.adoc[`DOCS.adoc`] | The documentation index and its maintenance rules.
| xref:target_arch/index.adoc[`docs/target_arch/`] | Invariants, capability boundaries, non-goals.
| xref:glossary.adoc[`docs/glossary.adoc`] | The load-bearing vocabulary (kairos, mētis, gate, deinotēs).
| `docs/prd/NNNN-*.adoc` | Per-capability requirements and the decisions behind them.
| xref:../src/metis/mod.adoc[`src/metis/mod.adoc`], xref:../src/kairos/mod.adoc[`src/kairos/mod.adoc`] | The implementer's view, co-located with the code.
| xref:kairos-minerva-reconciliation.adoc[`docs/kairos-minerva-reconciliation.adoc`] | The `kairos` / `plethos` convergence and its ADRs.
| xref:metis-gate-algebra.adoc[`docs/metis-gate-algebra.adoc`] | The gate algebra: composing gates (`And` / `MapDep`), why `Or` is excluded, the dynamic contract for authoring a gate.
| https://github.com/OwlRoute/minerva/blob/main/todos.adoc[`todos.adoc`] (repository only) | The living slice tracker: unresolved work only; completed slices live in git history.
|===