---
title: "Release 2026-08-16 - Changelog"
url: "https://nsta1.github.io/Orleans.Lattice/changelog/2026-08-16.html"
source: "https://github.com/NSTA1/Orleans.Lattice/blob/release/9.9/CHANGELOG.md?plain=1#L1645-L1655"
documents: "Orleans.Lattice 9.9.0 (release line 9.9)"
built: "2026-10-04"
all-pages: "https://nsta1.github.io/Orleans.Lattice/llms.txt"
---
# Release 2026-08-16

Part of the [changelog](../CHANGELOG.md).

A per-package patch advances `Orleans.Lattice` to `9.0.3` and `Orleans.Lattice.Replication` to `9.0.1` (see Fixed); all other packages remain at their `9.0.x`/`9.0.0` versions.

> **Action required - `Orleans.Lattice.Replication` 9.0.1 is fail-closed.** A silo that sets `WalRetention` (directly on `LatticeOptions`, or via the replication-side mirror) on a **replicated** tree will now **refuse to start** unless the anti-entropy digest probe (`DigestProbeEnabled`) is enabled or the new `LatticeReplicationOptions.AllowWalRetentionWithoutAntiEntropy` override (default `false`) is set. This closes a silent, permanent cross-cluster divergence path (see Fixed), but it means an operator running that exact combination must, **before** upgrading, either enable `DigestProbeEnabled` (recommended - the divergence then becomes observable and, with the repair stages on, self-healing) or set the override to acknowledge the risk. For a cluster group that has **already** silently diverged under this exact combination before upgrading, enabling the digest probe (with the repair stages on) is the valid remediation, not merely a preventative: the probe compares content digests out-of-band of the WAL, so it detects a divergence whose triggering entries have already been garbage-collected, and the bootstrap-fallback repair re-materialises the missing range - re-converging the affected cluster rather than only stopping new drift. Hosts that do not set `WalRetention` on a replicated tree - the default posture - are unaffected and upgrade as a drop-in patch.

## Fixed

- **A silo now refuses to start when `WalRetention` is set on a replicated tree without the anti-entropy detection backstop, closing a silent cross-cluster divergence path.** A WAL retention ceiling lets the sender's WAL GC trim entries a lagging cross-cluster shipper has not shipped yet (the TTL ceiling is a union with the consumer-cursor floor, so it trims past the shipper's pinned cursor). Unlike a local materialiser - whose next read surfaces the trimmed prefix to the auto-bootstrap trigger - the shipper advances past the trimmed prefix without emitting a fall-off-the-log event, and the receiver-side detector only compares against its own local WAL, so the receiver silently and permanently diverged for the trimmed range with no metric and no repair. A new fail-closed startup validator (`LatticeWalRetentionReplicationStartupValidator`) rejects the combination unless `DigestProbeEnabled` is enabled (the digest probe detects a garbage-collected divergence out-of-band) or the new opt-in `LatticeReplicationOptions.AllowWalRetentionWithoutAntiEntropy` acknowledgement (default `false`) is set. Effective retention is read from the per-tree core `LatticeOptions.WalRetention`, which already reflects any value mirrored from the replication-side `WalRetention`, so the rule catches retention configured on either surface; non-replicated trees are unaffected and the safe default posture (`WalRetention` unset) is unchanged. (`Orleans.Lattice.Replication` 9.0.1, [#1496](https://github.com/NSTA1/Orleans.Lattice/issues/1496), [#1499](https://github.com/NSTA1/Orleans.Lattice/pull/1499))
- **The WAL GC no longer trims a leaf's durably-checkpointed prefix that no durable snapshot covers, closing a cold-restart data-loss path that survived 9.0.2.** A partition can durably advance its checkpoint to offset `N` while the leaf has persisted no snapshot materialising the rows in `[0, N]`; the durable-materialiser pin then reported `(clock, N)`, authorising the shared-shard WAL GC to trim `[0, N]`. Because the leaf's per-activation projection cache is not persisted (it is rebuilt from the WAL on every activation), the next cold rebuild replayed from offset 0 over the trimmed WAL and silently lost the checkpointed prefix - a bounded but permanent loss per cold cycle, distinct from the unbounded-growth class fixed in 9.0.2. The durable pin is now coverage-gated: it authorises trimming only up to `min(checkpoint, snapshotCoveredOffset)` and reports the `(Zero, -1)` block pin for a partition whose checkpointed prefix is not yet snapshot-covered, so the WAL prefix is retained until a covering snapshot exists. Cadence capture is made unconditional (it proceeds whenever any partition has a checkpoint `>= 0`) and records per-partition snapshot offsets (`LeafSnapshotBlob.SnapshotOffsetsByPartition`, wire-compatible - legacy blobs decode with a null array and fall back to the scalar-only path byte-for-byte) so a busy non-zero partition is covered even when partition 0 is idle. A companion `LeafSnapshotStorageGrain.HasCapturedPrefix` guard recognises such a per-partition-only blob (any slot `>= 0` is loadable) across the load, size, and clear seams, so the partition-0-idle scalar `-1` sentinel no longer discards the sole durable copy on cold restart. Verified end-to-end against the local RepoContext container under repeated abrupt `SIGKILL`/restart cycles. (`Orleans.Lattice` 9.0.3, [#1492](https://github.com/NSTA1/Orleans.Lattice/issues/1492))

Newer release: [Release 2026-08-17](../changelog/2026-08-17.md). Older release: [Release 2026-08-15](../changelog/2026-08-15.md). Contents: [Changelog](../CHANGELOG.md).
