Table of Contents

Release 2026-09-26

This page is part of the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at 2026-09-26.md, and llms.txt lists every page.

Part of the changelog.

Coordinated lockstep release: 40 packages advance to 9.8.0 - Orleans.Lattice, Orleans.Lattice.Api.Abstractions, Orleans.Lattice.Api.Auth, Orleans.Lattice.Api.Auth.Grpc, Orleans.Lattice.Api.Backup, Orleans.Lattice.Api.Backup.Grpc, Orleans.Lattice.Api.Data, Orleans.Lattice.Api.Data.Grpc, Orleans.Lattice.Api.Mcp, Orleans.Lattice.Api.Mcp.Telemetry, Orleans.Lattice.Api.Mcp.Telemetry.Azure, Orleans.Lattice.Api.Replication, Orleans.Lattice.Api.Replication.Grpc, Orleans.Lattice.Api.Schema, Orleans.Lattice.Api.Schema.Grpc, Orleans.Lattice.Api.State, Orleans.Lattice.Api.State.Grpc, Orleans.Lattice.Api.Telemetry, Orleans.Lattice.Api.Telemetry.Grpc, Orleans.Lattice.Api.TenantAdmin, Orleans.Lattice.Api.TenantAdmin.Grpc, Orleans.Lattice.Api.TreeAdmin, Orleans.Lattice.Api.TreeAdmin.Grpc, Orleans.Lattice.Auth, Orleans.Lattice.Backup, Orleans.Lattice.Backup.AzureBlob, Orleans.Lattice.Caching.AzureBlob, Orleans.Lattice.Dashboards, Orleans.Lattice.GrainIndex, Orleans.Lattice.Membership, Orleans.Lattice.Membership.Entra, Orleans.Lattice.Membership.Entra.Graph, Orleans.Lattice.Membership.Oidc, Orleans.Lattice.Replication, Orleans.Lattice.Replication.Grpc, Orleans.Lattice.Scaling, Orleans.Lattice.Schema, Orleans.Lattice.Storage.AzureTable, Orleans.Lattice.Storage.File, Orleans.Lattice.Tenancy. Every package now requires its family dependencies at this version, superseding the 9.7.0 floors the 2026-09-24 patch left on packages that depend on a patched one. The Explorer family stays held at 9.4.x, and Orleans.Lattice.Vector, Orleans.Lattice.Api.Mcp.RepoContext and Orleans.Lattice.Api.Mcp.RepoContext.Replication remain unpublished to NuGet and are built from source; entries naming only those packages stay under ## Unreleased until they ship.

Upgrading from 9.7.x. A 9.8 silo widens a WAL materialiser pin shard's bucket layout once it outgrows its slot budget, and an older silo does not read the recorded width, so during a rolling upgrade either drain the older silos before the new build serves or run every silo at a WalMaterialiserPinBuckets of at least 2, ideally at or above the widest recorded width; a rollback after a shard has split needs the same care (#3576). Defaults that change: AzureTableWalStorageOptions.PhaseTwoCommitTimeout is 12 s (was 3 s), the ILattice worker pool is 256 per silo (was 32), and shard roots and WAL shards use random placement. WalBatchedSingleEntryAppends, WalAppendCoalescingInFlightThreshold (4), OptimisticShardRootPointReads, WalSaturationAcuteOnly and the saga registry admission bound TxRegistryAdmissionBudgetBytes (768 KiB) are on by default; each can be switched off through its option (false, 0, or null for the admission bound) to restore the 9.7 behaviour.

Added

  • Atomic - Saga decision registry admission bound. LatticeOptions.TxRegistryAdmissionBudgetBytes (default 768 KiB) refuses a new saga with LatticeSaturatedException before the per-tree registry row outgrows the provider entity limit, and new tx_registry instruments report its writes. (#3475) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Docs - Agent-readable site. Every page is also published as markdown, each package's docs as one file, and llms.txt lists every page. Oversized pages are split, source links resolve, the sitemap drops misdated entries, and each page states the release and version it documents. (#3571, #3577, #3578, #3586) (repository-wide)

  • WAL - Append coalescing. Under load a batched write left each WAL partition only a few entries, so every slice paid a full storage round trip. WalAppendCoalescingInFlightThreshold (default 4, 0 disables) lets a slice accumulate while that many flushes are in flight. (#3396) (Orleans.Lattice 9.8.0)

  • Performance - Layer 3 multi-silo scaling tier. performance-report.ps1 -Layer3 sweeps silo counts on an Azure Container Apps cluster, publishes the curve to performance-multi-silo.md and tears the cluster down. -Layer3ClientsPerSilo sets the producer clients per silo. (#3338, #3496) (repository-wide)

  • WAL - Runtime-configurable retention ceiling. A tree's WalMaxRetainedBytes ceiling can be raised or lowered at runtime through the tree-admin facade, taking effect without a host restart or redeploy; a tree with no override keeps its configured value. (#3333) (Orleans.Lattice 9.8.0, Orleans.Lattice.Api.Abstractions 9.8.0, Orleans.Lattice.Api.TreeAdmin 9.8.0, Orleans.Lattice.Api.Mcp 9.8.0)

  • Leaf - Orphan key census. An orphaned-leaf audit stopped at the first unverified key, so its verified prefix read as the extent of the damage. An opt-in survey now enumerates every key in the leaf and counts verified, missing and routing-contradiction outcomes separately. (#3293) (Orleans.Lattice 9.8.0, Orleans.Lattice.Api.Abstractions 9.8.0, Orleans.Lattice.Api.TreeAdmin 9.8.0, Orleans.Lattice.Api.TreeAdmin.Grpc 9.8.0, Orleans.Lattice.Api.Mcp 9.8.0)

  • Gates - Bucket closing list. A bucket pull request into the default branch is now checked to carry a closing reference for every issue its merged members claim, because a closing keyword on a member targeting a non-default base is silently inert. (#3320) (repository-wide)

  • WAL - Recovery discard telemetry. Activation-time recovery truncated unsealed tail bytes and dropped uncommitted records in silence, leaving a restart that discarded data with no attributable trace. Recovery now counts discarded bytes and records separately, primed at zero per shard. (#3378) (Orleans.Lattice 9.8.0, Orleans.Lattice.Storage.File 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Observability - Checkpoint flush tail faults are counted. Faults in the steps that follow a successful leaf checkpoint flush were contained silently. leaf.checkpoint.flush.tail.failures counts them by step, so a zero barrier count no longer passes for a healthy checkpoint path. (#3397) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

Changed

  • WAL - Point appends use the interleaving batched dispatch. With WalBatchedSingleEntryAppends (default on), a single-key write no longer holds the WAL shard turn for a storage round trip, so point writes stop serialising per partition, and no longer emit wal.append.turn_wait. (#812) (Orleans.Lattice 9.8.0)

  • WAL - Phase-two commit timeout default raised to 12 s. AzureTableWalStorageOptions.PhaseTwoCommitTimeout now defaults to 12 s instead of 3 s, above the ~6 s storage-account brown-outs that tripped every shard's timeout at once. (#3458) (Orleans.Lattice.Storage.AzureTable 9.8.0)

  • Shard - Optimistic point reads. GetAsync reads interleaved on the shard root, validated by a routing epoch the leaf reply carries, and falls back to the serial path only when routing moved, even under concurrent writes. OptimisticShardRootPointReads controls it. (#3474, #3570) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Core - ILattice worker pool raised from 32 to 256 per silo. The router grain's stateless-worker pool capped point traffic at about 32 calls in flight per silo. BoundedFanOut stays at 32, so one caller's burst cannot exhaust the larger pool. (#812) (Orleans.Lattice 9.8.0)

  • Atomic - Saga decision registry scales. TxRegistryGrain group-commits mutations that arrive during a write, so one round trip commits many sagas, and LatticeOptions.TxRegistryShardCount (default 1) splits the registry row into shards whose ceiling scales with the count. (#3475, #3501) (Orleans.Lattice 9.8.0, Orleans.Lattice.Backup 9.8.0, Orleans.Lattice.Replication 9.8.0)

  • Shard - Concurrent point writes per shard. SetAsync point writes on one shard no longer queue on the shard root's turn. Splits, routing changes and deactivation still exclude them; a write arriving during deactivation gets a retriable error the ILattice retry absorbs. (#812) (Orleans.Lattice 9.8.0)

  • Docs - Multi-silo scaling guide re-measured. performance-multi-silo.md reports true-throughput ceilings from one to eight silos at the Layer 3 epic tip, with no >= cells. No workload is flat any more, and each section states what bounds its curve. (#3496) (repository-wide)

Fixed

  • Leaf - Split forwards lost acknowledged keys. A leaf re-forwarding a key during an interrupted split sent it to the uninitialised sibling, which acknowledged it and later stranded it outside its span. Forwards now target the real successor, and recovery routes by declared span. (#3583) (Orleans.Lattice 9.8.0)

  • WAL - Materialiser pin store outgrew the entity limit. A pin shard now widens its bucket layout once a slot would pass 64 KiB, writes only changed slots, and backs off on failure. Rolling upgrade: drain older silos first, or run all silos at WalMaterialiserPinBuckets >= 2. (#3576) (Orleans.Lattice 9.8.0)

  • WAL - GC trimmed past an unreadable pin census. An unreadable pin slot now fails activation, and a GC pass that cannot read the pin or offset census trims nothing and reports BlockedByUnusablePin, instead of reclaiming a dormant leaf's un-checkpointed tail. (#3576) (Orleans.Lattice 9.8.0)

  • Leaf - Successful captures spent the zero-coverage repair budget. Each capture on a multi-partition leaf used one of its 8 repair attempts, so the leaf entered backoff while still pinning the WAL. A capture that reduces the uncovered partitions is now refunded. (#3576) (Orleans.Lattice 9.8.0)

  • Performance - Benchmark ingest accounting. Each atomic saga is its own flush unit, one failed point call fails only its own entry, point fan-out is bounded per silo (BENCH_POINT_FANOUT), and saturation refusals back off and retry instead of failing the batch. (#3339, #3474, #3581, #3590) (repository-wide)

  • Atomic - Saga grains wedged on a landed ETag conflict. A transport retry of a landed write reported a conflict and left the cached ETag stale. Saga, coordinator and pin grains now deactivate and fail fast so the next call reloads; provider faults surface as LatticeStateWriteFailedException. (#3572) (Orleans.Lattice 9.8.0)

  • Shard - Deactivation drains in-flight writes. A shard root that suspended its flush retries deactivated with interleaved writes still running. New writes are now fenced at once, and the runtime deactivation waits until admitted point and batch writes drain. (#3546) (Orleans.Lattice 9.8.0)

  • WAL - An abandoned phase-two submit raced the resync. A timed-out commit could still land while the resync read TAIL, causing overlap rejections and shard deactivations. The abandoned submit is now fenced until it settles (60 s cap), and a late success folds its TAIL. (#3458) (Orleans.Lattice.Storage.AzureTable 9.8.0)

  • Atomic - Router worker-pool self-deadlock under concurrent sagas. SetManyAtomicAsync held its stateless-worker turn while its saga called back through the same pool, so a silo whose workers all awaited sagas froze. The five saga-awaiting entry points now interleave. (#3475) (Orleans.Lattice 9.8.0)

  • Performance - Layer 3 sweep grading. -Layer3 escalates offered load until a cell stops keeping up, grades a mid-run freeze WEDGE, re-runs unseeded cohorts, clears cohort env between runs, generates load in parallel, and grades producer-bound only when the generator fell short. (#3496, #3589) (repository-wide)

  • Performance - Layer 3 read seeding. Read cohorts ran against an empty keyspace in cluster mode, and a failed preseed restarted from key 0. The producer now seeds before the measured window and resumes from its last landed slice; the report excludes unseeded cohorts. (#3474, #3588) (repository-wide)

  • WAL - Trim ran past a pinned consumer the floor misread. The retention floor read a leaf absent from the offsets plane as abstaining, and a consumer registered only with a zero cursor as covered, so a reap could trim entries either still owed. Both now guard their partition. (#2314, #3416) (Orleans.Lattice 9.8.0)

  • Leaf - Compacted tombstone resurrected by a monotone merge. MergeMonotone folded a stored-only key back into the leaf after tombstone compaction had removed it, resurrecting a deleted key. The merge now declines a key the leaf no longer declares. (#2436) (Orleans.Lattice 9.8.0)

  • Replication - Deactivation flush could outlive its deadline. The hook wrote its cursor even when its token began cancelled, and awaited a tokenless state write that could outrun the deactivation deadline. It now honours the token, and coverage drives it through Orleans rather than by hand. (#2544) (Orleans.Lattice.Replication 9.8.0)

  • Dashboards - Conditionally emitted instruments were zero-filled. A series published only under some conditions rendered as a measured zero rather than as absent, so a panel could not tell "nothing happened" from "nothing was reported". A committed gate re-derives that population from source. (#2520) (Orleans.Lattice.Dashboards 9.8.0)

  • Gates - Metric gates that could not detect what they claimed. The priming enrolment recorded none for tags its parser could not reach and conflated open-by-nature dimensions with unread tags, and a description arity claim naming no tag could never be falsified. (#3202, #3231, #3318) (Orleans.Lattice 9.8.0)

  • Backlog - An unrecognised state tag read as no state at all. The ready-set computation recognised only state:complete and state:parked, so any other value - four were in use - got the verdict an untagged item gets, offering finished work as claimable. The vocabulary is closed and fails safe. (#2468) (repository-wide)

  • WAL - A one-entry bulk append serialised its partition. It took the exclusive-turn overload, holding the partition for a whole provider round trip and pinning batch occupancy at 1, which under a wide fan-out is nearly every append. It now interleaves; WalBatchedSingleEntryAppends reverts it. (#3408) (Orleans.Lattice 9.8.0)

  • Core - Hot grains piled onto one silo. Shard roots and WAL shards burst-activate from the gateway silo and default placement kept them all there, so one silo held the tree. They now use random placement. (#3348) (Orleans.Lattice 9.8.0)

  • WAL - Saturation recovery released every parked caller at once. A recovered partition completed its whole parked population in one pass, which re-saturated it before any drain and left the gate flapping with no net progress. Release is now paced, oldest-first, and level-triggered. (#3402) (Orleans.Lattice 9.8.0)

  • Leaf - A batch reaching a splitting leaf wrote one key at a time. SetManyAsync on a leaf mid-split or straddling its span paid a serial WAL commit per key, so one branch ran for minutes. It now finishes the split once, forwards each out-of-span group as one batch and appends the rest once. (#3348) (Orleans.Lattice 9.8.0)

  • WAL - One failed flush could wedge its shard for good. The post-failure reconcile now waits out the shard's in-flight writes, never lowers TAIL, and keeps a committed batch it finds above TAIL; a failed resync deactivates the grain instead of latching the fault forever. (#3348) (Orleans.Lattice 9.8.0, Orleans.Lattice.Storage.AzureTable 9.8.0)

  • WAL - A partition at its admission cap closed the saturation gate. An at-cap partition now reads Throttled, not Saturated, to every consumer including scaling and health checks, and a gate-parked caller resumes once its partition leaves Saturated. WalSaturationAcuteOnly = false reverts it. (#3348, #3444) (Orleans.Lattice 9.8.0)

  • WAL - Azure accepted a batch overlapping a written one. Each batch has its own partition, so one starting inside a written batch collided on no row and its shared offsets read back twice. It is now rejected before any write, with no storage call on the in-order path. (#3457) (Orleans.Lattice.Storage.AzureTable 9.8.0)

  • Docs - Link check reported a count it never read. docfx colours its summary line on CI, defeating the anchored pattern parsing it, so the count kept its 0 default and -MaxWarnings 0 passed while two broken anchors shipped. It now reads stripped output, and an unreadable count fails. (#3406) (repository-wide)

  • WAL - In-memory provider reused offsets after a full trim. InMemoryWalStorageProvider restarted allocation at 0 once a trim removed every live entry. It keeps no durable state, so offsets were reused rather than data lost. IWalStorageProvider now states the high-water-mark contract. (#3401) (Orleans.Lattice 9.8.0)

  • WAL - Placement moves misread the high-water mark. A move back onto a reclaimed provider skipped offsets it no longer held, and a fully-trimmed partition moved without its mark, so the target reused offsets. Both now fail closed, and a repeated reclaim reports NoOp. (#3459, #3460) (Orleans.Lattice 9.8.0)

  • WAL - Purged tree resurrection. A WAL shard activating after its tree was purged resolved options through the lazy seeding path, re-creating the registry row and resurrecting the tree. The activation now uses the pure fast path, which never mutates the registry. (#3343) (Orleans.Lattice 9.8.0)

  • Leaf - Checkpoint hints and suppression accounting. A non-advancing projection-checkpoint hint was published at the leaf seam, and chain-regression suppressions were neither counted nor bounded, so a persistent regression flooded the log unmeasured. Both are now counted and bounded. (#3360, #3341) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Config - Per-tree retention ceiling. The storage-pressure collector read WalMaxRetainedBytes from the unnamed default options instance, so a silo configuring ceilings only per tree resolved a zero threshold and never fired its capacity signal. It now resolves per tree. (#3336) (Orleans.Lattice 9.8.0, Orleans.Lattice.Scaling 9.8.0)

  • Core - Domain faults absorbed by broad catches. Broad BCL catch clauses swallowed domain exceptions their callers were expected to handle, so a domain fault surfaced as a generic failure or was lost entirely. The narrowed clauses now let domain faults propagate. (#3361) (Orleans.Lattice 9.8.0, Orleans.Lattice.Replication 9.8.0)

  • Tests - Gates that measured the wrong population. Three gates sampled too narrow a set to detect the defect they existed for: a chaos partition was never three-way, dashboard enrolment keyed on meter identity, and incidental key collectors used non-recovering scans. (#3125, #2811, #3355) (repository-wide)

  • Replay - Latched admission gate. A cold-replay storm drove the process-wide smoothed permit queue wait above the refusal bound, and refused arrivals never sample, so nothing could fold it back: the gate shed activations for the rest of the process lifetime. A stale mean is now discarded. (#3306) (Orleans.Lattice 9.8.0)

  • Atomic - Conditional multi-key completeness. ConditionalSetManyAsync silently dropped keys whose row a split had moved to a sibling leaf, because the guard read a cache miss as non-matching. Declared-span admission now runs before the guard, so a key is evaluated on the leaf that declares it. (#2663) (Orleans.Lattice 9.8.0)

  • CRDT - Full-state merge read form. A replication full-state merge read local state through the client read path, which strips the schema envelope and upcasts, then folded and wrote that decoded value back. The merge now reads the stored form through the replication-apply seam. (#2813) (Orleans.Lattice 9.8.0, Orleans.Lattice.Replication 9.8.0)

  • Tests - Guards that could not fail. Four gates missed the defect they existed for: a sweep-count guard passed on its own failure mode, the leaf cursor reporter was unpinned to the GC floor, a trim fixture asserted 0 == 0, and New-TuningEnv refusals were unreachable in CI. Each now fails. (#2656, #3310, #3209, #2832) (repository-wide)

  • WAL - Batch fan-out waited out every shard branch. SetManyAsync awaited its slowest branch without bound. A faulted branch now surfaces at once, and the opt-in SetManyFanOutBudget refuses a slow fan-out with LatticeSaturationSource.SetManyFanOut; committed branches stay committed. (#3348) (Orleans.Lattice 9.8.0)

  • WAL - One busy partition refused appends to the whole tree. The admission gate decided on the tree-wide saturation verdict, so one hot partition refused appends routed to idle ones. The gate is now partition-scoped, and genuinely tree-wide causes still floor every partition. (#3348) (Orleans.Lattice 9.8.0)

  • WAL - A call's total gate wait was unbounded. Nested retry layers each opened a fresh per-wait budget, so one call could wait a multiple of it. The opt-in WalAdmissionSaturationCallBudget bounds the total wait per top-level call; its infinite default preserves the old behaviour. (#3348) (Orleans.Lattice 9.8.0)

  • WAL - Throttled pacing taxed every partition because one was busy. Throttle pacing still read the tree-wide verdict after the gate was narrowed, delaying appends to idle partitions. Pacing is now partition-scoped, and the tree-wide floor applies only during the recovery window. (#3348) (Orleans.Lattice 9.8.0)

  • Replay - Permit refusal failed the caller instead of shedding it. Nothing retried a replay-admission refusal, so it aborted activation and surfaced as a hard error. The shard-dispatch envelope now retries it on a jittered 2 s/4 s ladder, keyed by a new LatticeSaturationSource discriminator. (#3294) (Orleans.Lattice 9.8.0)

  • Leaf - A declined snapshot capture advanced durable coverage. When the snapshot store declined a capture, the leaf still recorded its coverage as durable, licensing WAL GC to trim past anything a snapshot can reproduce. Coverage now advances only when the store keeps the capture. (#3421) (Orleans.Lattice 9.8.0)

  • Leaf - Stale projections no longer loop or pin the WAL. A leaf trimmed past its checkpoint with no covering snapshot latches one Error per activation instead of re-driving every stall window. A warm cache proven complete is re-snapshotted, so its checkpoint advances and WAL GC resumes. (#3450, #3454) (Orleans.Lattice 9.8.0)

  • Shard - A removed leaf's failed state clear was never retried. Reclaim swallowed a failed clear of a removed leaf's grain state, orphaning it for good. The owed clear is now recorded durably and retried by later reclaim passes, orphan repair and purge. (#2207) (Orleans.Lattice 9.8.0)

  • Leaf - Tombstone ratio minted a series per leaf. orleans.lattice.leaf.tombstone.ratio was tagged with the leaf grain id, so its cardinality grew with the tree. It now carries only tree and tenant: one series per tree. (#2518) (Orleans.Lattice 9.8.0)

  • Backlog - Protocol gaps that hid stuck work. A claim drawn without a marker read as never attempted, and an item could wait forever on a parked blocker. Attempts are now cross-checked against the fencing token, and parked blockers are rejected, reported as stalled and routed to a ruling owner. (#2466, #2529) (repository-wide)

  • Gates - Deep-copy contract enrolment was unchecked. Nothing required a package declaring a serializable exception to enrol the same-silo deep-copy guard, and one had not. Every package must now enrol the guard or be verified exempt from source. (#2448) (repository-wide)

  • Auth - An exact-key deny hid a readable prefix grant. The existence probe judged a prefix grant at the prefix string itself, so a deny on that one key hid a tree whose other keys under the prefix stayed readable. A prefix grant is now judged as enforcement judges the keys beneath it. (#3467) (Orleans.Lattice.Auth 9.8.0)

  • Core - A failed view unregister was never retried. A runtime view delete that hit a storage fault dropped the view from memory only, so a retried delete reported success while storage kept it and the next silo start restored the view. A failed write now restores the entry. (#3469) (Orleans.Lattice 9.8.0)

  • WAL - A never-written leaf held a pin no drive could lift. Its (Zero, -1) pin left the GC scheduler looping on NoAdvance for ever. The leaf now publishes its persisted scanned-through checkpoint, and a zero pin on a proven-empty partition still blocks trim but is no longer reported. (#3453) (Orleans.Lattice 9.8.0)

  • WAL - A graceful deactivation lost its final pin. The slow digest publish ran first and used up the deadline, so the capture and pin barriers faulted on a torn-down activation. The final pin now publishes first, torn-down barriers skip, and the digest publishes last. (#3393) (Orleans.Lattice 9.8.0)

  • Leaf - A latched stale leaf was not terminal. A split stamped a checkpoint hint over a stale-latched partition, so it later replayed past the gap it had declared unrecoverable, and WAL GC re-drove every latched leaf each cooldown. The hint is refused and a latched verdict is terminal. (#3477, #3478) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Shard - Empty-leaf reclaim under-counted and overran. Reclaim counted one of its four probe sites, so a reported probed 0 could hide a spent budget, and never checked its deadline before entry. Every probe now counts and entry stands down at the deadline. (#2682) (Orleans.Lattice 9.8.0)

  • Tests - Probes and fixtures that could not fail. The async allocation probe could report zero for a loop that allocates, and leaf fixtures modelled an unreachable WAL state, so guards on those paths passed without exercising them. Both now measure what they claim. (#3419, #2680) (repository-wide)

  • Tests - Two guards blind to the regressions they named. The metric arity gate skipped claims spelling a count above twelve, and the guard that the host builds its GC meter eagerly passed on a lazy factory registration. Both now fail on the shape they exist to catch. (#3525) (repository-wide)

  • Leaf - Bounded split transfer was inert on a detached leaf. An empty boundary list meant four different things and the consumer read each as 'one batch is fine', so a detached leaf shipped its split unbounded. The transfer is now bounded there too. (#2848) (Orleans.Lattice 9.8.0)

  • Leaf - Out-of-span keys committed locally in silence. Span admission failed open unmeasured when no neighbour declared a key, and a merge group built before a split still shipped whole to its first leaf. Fail-open commits are now counted and a retried or re-routed group re-groups per leaf. (#2125) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Shard - Concurrent writes orphaned split leaves. Interleaved turns linked each split into a parent from a path captured before descent, so a parent divided or promoted meanwhile took a key outside its range and the new leaf's acknowledged writes were lost. Splits now link by fresh descent. (#3523) (Orleans.Lattice 9.8.0)

  • Shard - Split linking stalled behind a digest publish. A child's digest publish waited on its parent's split gate while the holder waited on that child, until a timeout dropped the split. Contended publishes are now parked and folded on release; a parent's own split links before later divisions. (#3523) (Orleans.Lattice 9.8.0)

  • Tests - Split routing convergence. Conditional-write misses that survived quiesced retries came from leaves a split left without a separator. The chaos leg now audits for such leaves and reads every key through routing after the quiesce, and unit tests pin every forward's division. (#3358) (repository-wide)

  • WAL - GC blocked-consumer sweep published no census. The blocked-leaf sweep reported nothing about how many consumers held the trim floor back, so convergence was unobservable. An uncapped blocked-consumer census gauge now publishes it. (#3161) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • WAL - Phantom drain lag from idle leaf cursors. A fresh cursor report could carry a position that had not moved in days, so _lattice_trees showed impossible drain lag. Drain lag now excludes position-stale cursors, judged by the new WalCursorSnapshot.CursorAdvancedAtTicks. (#3131) (Orleans.Lattice 9.8.0)

  • Observability - Registry contention went unmeasured under saturation. Diagnosis relied on the CurrentlyExecuting block Orleans appends to call timeouts, which stops once a silo saturates, so a wedged registry read as idle. Callers now record registry.caller.duration by method and outcome. (#3088) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Leaf - Lifting a moved-away seal lost cached rows. Keys reclaimed while a leaf was sealed were not re-shipped to its leaf caches when the seal lifted, so a cache could answer null for a present key. The lift now re-ships them. (#3524) (Orleans.Lattice 9.8.0)

  • Leaf - Deferred-terminal cap metric described a drop that never happens. leaf.deferred_terminals_dropped_at_cap said terminals were dropped at the ledger cap; only their durable ledger record is refused, and pass 2 still drains them. Its description, docs and CommitPath panel now say so. (#3190) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Replay - Starvation drives crowded out other replay work. GC drives could fill the replay-permit queue, and leaf timer drives starve the WAL GC sweep. Drives now take a bounded, non-queueing share that keeps a slot for the sweep; refusals back off instead of faulting, and abandonment logs why. (#3479, #3480, #3575) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)

  • Storage - A failed fsync left an unacknowledged tail in the file WAL. The provider now truncates and flushes away the unacknowledged tail, fail-stopping the shard if that rollback fails, and a flush-ordering seam lets a test fail when fsync-before-ack breaks. (#3462) (Orleans.Lattice.Storage.File 9.8.0)

  • Views - View creation failed during reminder-service startup. The view maintainer registered its keepalive reminder unguarded. It now waits out the transient startup fault with the shared bounded retry, and a persistent or unrelated fault still surfaces. (#3558) (Orleans.Lattice 9.8.0)

  • Tests - Three tests flaked on harness timing. The production-shipper fixture clears its startup backoff before tests run, restore reconciliation is pinned by a deterministic regression across registry scan aborts, and the saga registry liveness test injects write faults on a fixed schedule. (#3233, #3337, #3563) (repository-wide)

  • Replay - Leaf replay decoded every record of a shared WAL partition. Each leaf decoded every other leaf's records before discarding them, costing heavy GC on large trees. A new optional IWalStorageProvider.ReadFilteredAsync seam lets providers skip records the leaf does not own. (#3565) (Orleans.Lattice 9.8.0, Orleans.Lattice.Storage.File 9.8.0, Orleans.Lattice.Storage.AzureTable 9.8.0)

  • Core - New workers postponed tombstone compaction. Each new LatticeGrain worker re-registered its tree's compaction reminder on its first write, moving the next pass a full period out, so a tree that kept getting new workers never compacted. It now registers only when absent. (#3592) (Orleans.Lattice 9.8.0)

  • Leaf - A frozen checkpoint lost acknowledged writes. Only a never-checkpointed leaf was repaired, so a leaf whose checkpoint stopped advancing acknowledged writes that compaction reclaimed and a restart lost. Such a leaf is now driven to replay too. (#3389) (Orleans.Lattice 9.8.0)

  • WAL - Shutdown pin flushes were dropped. A pin write rejected during silo shutdown falls back to a direct state write, but a newer Orleans rejection shape bypassed it, so each restart lost every leaf's final frontier and retained WAL grew without bound. That shape now falls back too. (#3382) (Orleans.Lattice 9.8.0)

  • Leaf - One failed teardown step skipped the rest. Leaf teardown ran its digest, checkpoint, snapshot and pin steps under one catch, so a failed checkpoint flush skipped the snapshot that keeps unsaved rows. Each step now runs alone; leaf.deactivation.barrier.failures counts faults by step. (#3387) (Orleans.Lattice 9.8.0, Orleans.Lattice.Dashboards 9.8.0)