Release 2026-10-02
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-10-02.md, and llms.txt lists every page.Part of the changelog.
Coordinated lockstep release: 40 packages advance to 9.9.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.8.0 floors the 2026-09-29 and 2026-09-30 patches left on packages that depend on a patched one. Held-back packages are not part of this wave: the published Explorer packages (Orleans.Lattice.Explorer.Core, Orleans.Lattice.Explorer.UI, Orleans.Lattice.Explorer.Web, Orleans.Lattice.Explorer.Entra, Orleans.Lattice.Explorer.Entra.Web) stay held at 9.4.x on release/9.4, and Orleans.Lattice.Explorer.AppKit, Orleans.Lattice.Apps, Orleans.Lattice.Api.Apps, Orleans.Lattice.Api.Apps.Grpc, Orleans.Lattice.Api.Mcp.Apps, Orleans.Lattice.Api.Mcp.RepoContext, Orleans.Lattice.Api.Mcp.RepoContext.Replication and Orleans.Lattice.Vector remain unpublished to NuGet. Entries naming only those packages stay under ## Unreleased until they ship, and an entry that also touched one of them names here only the packages this wave ships.
Deprecated in 9.9.0. The blocking long-running verbs below raise warning LATTICE0002 in favour of accept-then-poll operations, which return a handle at once and are polled for status and progress. They still work in 9.9.0 and will be removed in the next major version. Each is listed with its replacement:
- Backup -
ILatticeBackupControl(Orleans.Lattice.Api.Abstractions, implemented byOrleans.Lattice.Api.Backup), replaced byILatticeBackupOperationspolled withGetOperationStatusAsync:CreateBackupAsync->StartBackupAsync,CreateIncrementalBackupAsync->StartIncrementalBackupAsync,CreateBackupSetAsync->StartBackupSetAsync,RestoreBackupAsync->StartRestoreAsync,ColdRestoreAsync->StartColdRestoreAsync,CheckBackupHealthAsync->StartBackupHealthCheckAsync,RebuildCatalogFromSinkAsync->StartCatalogRebuildAsync,ScrubCatalogAgainstSinkAsync->StartCatalogScrubAsync. - Backup gRPC -
LatticeBackupApiGrpcClient(Orleans.Lattice.Api.Backup.Grpc), polled withGetBackupOperationStatusAsync:CreateBackupAsync->StartBackupAsync,CreateIncrementalBackupAsync->StartIncrementalBackupAsync,CreateBackupSetAsync->StartBackupSetAsync,RestoreBackupAsync->StartRestoreAsync,CheckBackupHealthAsync->StartBackupHealthCheckAsync; and the RPCsCreateBackup,CreateIncrementalBackup,CreateBackupSet,RestoreBackupandCheckBackupHealth->StartBackup,StartIncrementalBackup,StartBackupSet,StartRestoreandStartBackupHealthCheck. - Schema -
ILatticeSchemaControl(Orleans.Lattice.Api.Abstractions, implemented byOrleans.Lattice.Api.Schema), polled withGetOperationStatusAsync:RemediateAsync->ILatticeSchemaOperations.StartRemediationAsync,MigrateToTargetVersionAsync->ILatticeSchemaOperations.StartMigrationAsync,AdvanceAndMigrateAsync->ILatticeSchemaOperations.StartAdvanceAndMigrateAsync,ScanComplianceAsync->ILatticeSchemaComplianceOperations.StartComplianceScanAsync. - Schema gRPC -
LatticeSchemaApiGrpcClient(Orleans.Lattice.Api.Schema.Grpc):RemediateAsync->StartRemediationAsync,MigrateToTargetVersionAsync->StartMigrationAsync,AdvanceAndMigrateAsync->StartAdvanceAndMigrateAsync, each polled withGetSchemaOperationStatusAsync, andScanComplianceAsync->StartComplianceScanAsync, polled withGetComplianceScanStatusAsync; and the RPCsRemediate,MigrateToTargetVersion,AdvanceAndMigrateandScanCompliance. - Tree admin -
ILatticeTreeAdmin(Orleans.Lattice.Api.Abstractions, implemented byOrleans.Lattice.Api.TreeAdmin), replaced byILatticeTreeAdminOperationspolled withGetOperationStatusAsync:RebuildViewAsync->StartViewRebuildAsync,ReconcileViewAsync->StartViewReconcileAsync,ReconcileTagIndexAsync->StartTagIndexReconcileAsync,ExecuteWalMoveAsync->StartWalMoveAsync. - Tree admin gRPC -
LatticeTreeAdminApiGrpcClient(Orleans.Lattice.Api.TreeAdmin.Grpc), polled withGetTreeAdminOperationStatusAsync: the same four methods -> the same fourStart*Asyncmethods; and the RPCsRebuildView,ReconcileView,ReconcileTagIndexandExecuteWalMove. - MCP tools (
Orleans.Lattice.Api.Mcp), which will be removed in the next major version:lattice_backup_create->lattice_backup_start,lattice_backup_create_incremental->lattice_backup_start_incremental,lattice_backup_restore->lattice_backup_start_restore,lattice_treeadmin_schema_remediate->lattice_treeadmin_schema_remediation_start,lattice_treeadmin_schema_migrate_to_target->lattice_treeadmin_schema_migration_startandlattice_treeadmin_schema_advance_and_migrate->lattice_treeadmin_schema_advance_and_migrate_startare now aliases of their start tools, so they return an operation handle instead of blocking;lattice_treeadmin_view_rebuild->lattice_treeadmin_view_rebuild_start,lattice_treeadmin_view_reconcile->lattice_treeadmin_view_reconcile_start,lattice_treeadmin_tag_index_reconcile->lattice_treeadmin_tag_index_reconcile_startandlattice_treeadmin_wal_move_execute->lattice_treeadmin_wal_move_startstill block.
Added
Core - Silos check that grain storage enforces ETags. Lattice requires its grain storage provider to reject a write carrying a stale ETag. Each silo now probes this as it starts and warns, or fails start in
Rejectmode, when it does not. (#4200) (Orleans.Lattice9.9.0)Admin - Compliance scans and fresh storage usage run in the background. Start either and poll its progress in entries or trees; it outlives a caller timeout, and Explorer shows its progress. The blocking compliance scan is deprecated (
LATTICE0002). (#4126) (Orleans.Lattice.Schema9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Schema9.9.0,Orleans.Lattice.Api.Schema.Grpc9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Schema - Accept-then-poll remediation and migration. Remediations and migrations return a handle at once and run in the background; follow the dry run, build and cutover in values processed, cancel before cutover, and find a run again after closing the tab. MCP tools start and poll them too. (#4123, #4209) (
Orleans.Lattice.Schema9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Schema9.9.0,Orleans.Lattice.Api.Schema.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Admin - See when a tree's WAL reclamation is wedged. A new read names the pin holding a tree's WAL floor, its leaf, pin offset and checkpoint, and flags a stranded pin that will not clear on its own. Explorer shows it on the WAL page and a tree's Storage tab. (#4195) (
Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0)Backup - Accept-then-poll backup and restore. Captures and restores return a handle at once and run in the background; poll phase and progress in entries, shards, members or manifests. They outlive a caller timeout or closed tab, a lost silo reads Failed, and cold restore works over gRPC. (#4122, #4218) (
Orleans.Lattice9.9.0,Orleans.Lattice.Backup9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Backup9.9.0,Orleans.Lattice.Api.Backup.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Backup - Health checks and catalogue rebuild and scrub run as operations. Start a backup health check, a catalogue rebuild or a scrub and poll it for the artifacts or manifests checked; the Explorer's Health and Maintenance pages follow it, and it outlives a caller timeout or a closed tab. (#4125) (
Orleans.Lattice.Backup9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Backup9.9.0,Orleans.Lattice.Api.Backup.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Core - Shared long-running operation contract.
Orleans.Lattice.Api.Operationsgives every facade one handle, status, list and cancel shape with an open operation kind, backed by one coordinator; backup is its first adopter. (#4122) (Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0)Admin - Accept-then-poll maintenance. View rebuild and reconcile, tag-index reconcile, WAL moves and orphaned-leaf audit and repair return a handle at once and report phase and unit progress; they survive a closed tab, the Explorer follows them, and they can be cancelled. (#4124) (
Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)WAL - Tell a harmless never-checkpointed pin from one that can wedge a tree. A new metric splits never-checkpointed WAL floor holders by whether the pin carries a real offset, so a benign sentinel population is no longer indistinguishable from one that can block a tree's WAL reclaim. (#4198) (
Orleans.Lattice9.9.0)Admin - Operation progress. Resize, snapshot and reshard statuses now report the step they have reached and their progress in shards or units, and the Explorer draws it as a progress bar, follows an accepted undo or purge until it finishes, and shows when a deleted tree stops being recoverable. (#3958) (
Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0)Core - Structural predicate kinds.
TypeOf,Length,EveryandSelfnodes test a value's shape: itsLatticeValueKind, a string, array or object length, or every item of an array. They are additive on the wire; an older silo evaluates an unknown kind as false, so a rule fails closed. (#3963) (Orleans.Lattice9.9.0)Explorer - Schema rule builder. The Schema area composes a policy from cards over the tree's inferred shape, previews it against up to 100 sampled values with the new public
LatticeSchemaPolicyValidator, and warns before saving a policy they would break. An Advanced view keeps the JSON. (#3963) (Orleans.Lattice.Schema9.9.0)Core - Ownership-bounded aliasing. An optional
ITreeOwnershipGuardcan refuse an alias that would cross tree ownership, for every alias change including resize, restore and remediation; a refusal throwsLatticeTreeOwnershipDeniedException. (#3766) (Orleans.Lattice9.9.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0)Gates - First-party namespaces cannot shadow Orleans. A hygiene gate fails when an
Orleans.Lattice.*namespace segment matches an Orleans framework namespace, the defect that let a newRuntimenamespace break name resolution in a package the change never touched. (#2822) (repository-wide)Gates - Deferred durable progress stays banked on the fault path. A gate fails when a banking helper is unreachable from a
catch, or swallows the fault it banked under: Orleans skipsOnDeactivateAsyncwhenOnActivateAsyncthrows, so progress flushed only on teardown is lost and redone. (#2545) (repository-wide)Retrieval - Exact-scan cost instruments. Exact kNN gathers publish returned vectors, pages, cumulative wall seconds, outcomes and budget evaluations under
repocontext.retrieval.exact_scan.*, charted on the overview dashboard, so exact-versus-ANN contention is measurable. (#3153) (Orleans.Lattice.Dashboards9.9.0)Shard - Online reshard shrinks.
ReshardAsyncaccepts a count below a populated tree's current one and folds adjacent shards together online, completing only once the retired shards' storage is released; theshrink_unsupportedrejection reason is gone. (#4059) (Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Admin - WAL reclamation over MCP.
lattice_treeadmin_wal_reclamationserves the wedged-WAL read to agents: the pin holding a tree's floor, its leaf and offsets, and whether it is stranded. (#4237) (Orleans.Lattice.Api.Mcp9.9.0)Observability - WAL GC records why it removed a pin. Every orphan-pin removal carries a
causeand writes an audit log line, every fail-safe refusal is counted, andorleans.lattice.wal.gc.drive_orphan_pin_retirementcounts the blocked-leaf drive's removal decisions. (#4246, #4242) (Orleans.Lattice9.9.0,Orleans.Lattice.Dashboards9.9.0)
Changed
Performance - CRDT delta apply path. Applying a delta walked six incoming lists through a boxed enumerator, rebuilt both OR-set alternate lookups once per dot, and grew the RGA node list and dot index unsized. All three are fixed: 51-69% off the scans, 16-23% fewer bytes per RGA merge. (#4281) (
Orleans.Lattice9.9.0)Performance - Delta run-fold sizing. Two OR-set union sizing passes routed their count selector through a capturing adapter lambda, minting a display class and a delegate hop per run element. A generic helper drives the selector directly: 24 B to zero, and 28% off the fold at width 32. (#4244) (
Orleans.Lattice9.9.0)Performance - Multi-value register dot probes. The duplicate-dot probe returned
MvRegisterEntry?, copying a 24-byte struct twice per candidate and running once per local entry per merge. Both probes return an index over a span now, still allocation-free and 27-29% faster at 32 entries. (#4244) (Orleans.Lattice9.9.0)Performance - Flag dot union span walk.
OrFlagandRwFlagunioned their dot lists through a list enumerator. Both walkCollectionsMarshal.AsSpanby reference now, taking 24% off the scan at 32 dots, with a self-union guard that keeps an aliased append safe. (#4244) (Orleans.Lattice9.9.0)Performance - WAL batch buffer pooling. The two leaf merge paths staged their record batch in a list, and the CRDT apply path copied its list to a fresh array before dispatch. All three take a threshold-gated pooled rental now: 82 KB to zero on a 256-record batch. (#4224) (
Orleans.Lattice9.9.0)Performance - Set accessor key probes. Two set accessors materialised a base64 string purely to probe a dictionary, then dropped it. Both encode into a stack or pooled span and probe through an alternate lookup now, allocating nothing on either side of the stack threshold. (#4224) (
Orleans.Lattice9.9.0)Performance - Sort comparer sweep completed. Sorting with an
IComparer<T>still minted a delegate per call at the sites #4184 left behind. Two hot streaming scan paths, sixteen downstream ordinal sites and two custom comparers pass a cached comparison now. (#4224) (Orleans.Lattice9.9.0,Orleans.Lattice.Api.Auth9.9.0,Orleans.Lattice.Api.State9.9.0,Orleans.Lattice.Api.TenantAdmin9.9.0,Orleans.Lattice.Tenancy9.9.0)Performance - Sort comparer delegate trims. Sorting with an
IComparer<T>minted a freshComparison<T>delegate per call, as the runtime converts its method group rather than caching it. Thirty-six CRDT, primitive and grain sort sites pass a cached comparison now and allocate nothing. (#4184) (Orleans.Lattice9.9.0)Performance - Leaf digest field appends. The per-entry digest contribution made four separate hash appends for one contiguous field block, and a vector clock appended its two fields separately per replica. Both stage into one buffer and append once now: 62% faster on the isolating lane. (#4181) (
Orleans.Lattice9.9.0)Performance - Leaf digest string feeding. Feeding a string to the digest appended its length prefix separately from the body, and sized its staging buffer to the key's own worst case, emitting a variable
locallocper call. It stages both into one constant-size buffer now: 20-46% faster. (#4181) (Orleans.Lattice9.9.0)Performance - Vector clock digest folding. Folding a multi-replica clock sorted a rented key array then re-looked-up every clock, and the keys-only
Array.Sortoverload allocated a comparer delegate per call. A paired-array sort carries clocks along now: 14-34% faster, 96% less allocated. (#4181) (Orleans.Lattice9.9.0)Performance - Pooled return prefix clearing. Five more pooled staging sites returned their rental with
clearArray: true, memsetting the whole rounded-up bucket rather than the slots written. They clear exactly the written prefix now: 50% faster on a sparse 4096-slot rental. (#4181) (Orleans.Lattice9.9.0,Orleans.Lattice.Storage.File9.9.0)Agents - Delegated sessions run targeted tests only. The agent playbooks and testing master bind a sub-session to a named fixture or method filter, never a whole test project, because concurrent sessions contend superlinearly and an unscoped run can perturb a co-located rig. (#4130) (
repository-wide)Performance - Pooled buffer return clearing. Nine pooled staging sites returned their rental with
clearArray: true, which memsets the whole rounded-up array rather than the bytes written. They clear exactly the written prefix now: 28-32% faster on a 4 KB to 64 KB staging call. (#4137) (Orleans.Lattice9.9.0,Orleans.Lattice.Membership9.9.0,Orleans.Lattice.Storage.File9.9.0)Performance - Identity digest allocations. Three SHA-256 identity paths staged input or digest bytes through throwaway arrays. They hash from stack or pooled buffers now: 72-88% less allocated on the credential metadata digest, 69-91% on the Explorer cookie digest, 16-27% on backup artifacts. (#4094) (
Orleans.Lattice.Membership9.9.0,Orleans.Lattice.Backup9.9.0)Performance - Leaf snapshot vector clock decode. Decoding a row's version vector read its exact entry count, then grew a default-sized dictionary into it one rehash at a time. It presizes from that count instead: 29% faster and 28% less allocated at four replicas, 40% and 38% at sixteen. (#4079) (
Orleans.Lattice9.9.0)Performance - Aggregation splice key handling. Splicing an entry into a group row transcoded its source key three times: once into a scratch buffer, once to size it and once to write it. It now reuses the first transcode, and sizes that buffer to the key: 44% to 57% faster on that work. (#4079) (
Orleans.Lattice9.9.0)Performance - Fold membership back-pointer write. A custom-fold contribution that keeps its group rewrote its membership row every time, though that row is a pure back-pointer whose bytes cannot have changed. It skips the identical write: one store round trip saved, 20% faster. (#4079) (
Orleans.Lattice9.9.0)Performance - Aggregation inverse row splice. The splice's second pass re-walked and re-parsed every entry purely to re-derive byte spans its first pass had already measured, then copied each entry one by one. It now block-copies the runs either side of the match: 42% faster on a 64-entry row. (#4062) (
Orleans.Lattice9.9.0)Performance - Aggregation fold row splice. The fold splice re-walked the same way, and each entry it stepped over also carried an opaque value payload whose length prefix the walk read only to skip. The same block copy makes it 44% faster on a 64-entry row. (#4062) (
Orleans.Lattice9.9.0)Performance - Unsharded aggregation slot gather. Materialising a group gathered its slots through a batched read, a batch of one at the default fanout: a list to hold one key and a map to hold one row, both dropped at once. It reads that slot directly instead: 55% faster, 57% less allocated. (#4062) (
Orleans.Lattice9.9.0)Performance - Aggregation same-group re-contribution. A min, max or set-union contribution that keeps its group sent its retraction and its addition to the same shard row, so the applier read, spliced and wrote that row twice. One fused splice now does it: 44% faster, 48% less allocated. (#4010) (
Orleans.Lattice9.9.0)Performance - Aggregation fold re-contribution. The custom-fold contribution path fuses the same way, and saves more: each redundant row walk also copied every member's opaque value payload. The fused splice is 46% faster and allocates 48% less. (#4010) (
Orleans.Lattice9.9.0)Performance - History view drain reshape. The history drain re-serialised every row it shaped, including the rows retention left untouched, reproducing bytes it already held. It now keeps the original bytes when shaping is a no-op: 47% faster and 49% less allocated on a CRDT delta row. (#4010) (
Orleans.Lattice9.9.0)Performance - Aggregation contribution row splice. Changing one entry of a group shard decoded the whole row into a map and re-encoded it, and every membership read decoded a member no caller uses. Rows are spliced in place instead: up to 69% faster and 79% less allocated. (#3981) (
Orleans.Lattice9.9.0)Performance - Aggregation group re-materialise. Re-folding a group decoded every shard into a keyed map, materialising a source-key string per entry that the fold never looks up. It now walks the row directly: the min/max gather allocates nothing at all, and the set-union gather 59% less. (#3950) (
Orleans.Lattice9.9.0)Performance - Aggregation saga operation id. Every numeric contribution and retraction interpolated a payload string purely to transcode it into the buffer that hashes it, then built the id from three more. It now composes those bytes in place and formats once, a third faster. (#3950) (
Orleans.Lattice9.9.0)Docs - Multi-silo guide scope. The multi-silo scaling guide now states that its figures come from one tree on one storage account and links multi-account fan-out, the Operate track lists it, and the internal
benchmark/notes are no longer published on the docs site. (#3617) (repository-wide)Backlog - Outcome-comment fields are named as such. The backlog protocol now says at first use that
result=releasedis a field of the outcome comment on the mirrored issue, not arepocontext_release_claimargument, and names the outcome comment consistently. (#2463) (repository-wide)Docs - Naming registry covers RepoContext. The naming-conventions registry gains its missing
Orleans.Lattice.Api.Mcp.RepoContextsection, and a gate fails when that section drifts from the package's public types. (#2494) (repository-wide)Performance - Clean leaf deactivations skip an acknowledged pin flush. The
frontier_pinbarrier is elided when this deactivation already acknowledged a dominating pin, counted byorleans.lattice.leaf.deactivation.barrier.elided; a faulted pin write no longer counts as acknowledged. (#3643) (Orleans.Lattice9.9.0,Orleans.Lattice.Dashboards9.9.0)Shard - Folds release the retired donor's storage. A committed shard consolidation, healing's included, clears the donor's leaves and internal nodes, freeing their WAL pins; the donor stays a routing tombstone, and no fold takes a shard an in-flight split still drains into. (#4059, #4104) (
Orleans.Lattice9.9.0)CI - One Explorer test lane, and no duplicate push runs. The duplicate Explorer CI workflow is retired, and the UI Tests and Videos push lanes skip a member branch's push, which its pull request run already covers. (#4260) (
repository-wide)Docs - Agent API specs list the operation RPCs.
docs/agents/api/schema.jsonandbackup.jsondescribe the accept-then-poll operation RPCs and mark the blocking ones deprecated. (#4210, #4239) (repository-wide)
Deprecated
Schema - Blocking remediation and migration verbs.
RemediateAsync,MigrateToTargetVersionAsync,AdvanceAndMigrateAsyncand their gRPC RPCs raiseLATTICE0002and will be removed in the next major version; start the run as a schema operation instead. (#4123) (Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Schema.Grpc9.9.0)Backup - Blocking backup verbs. The blocking capture, restore, health-check and catalogue rebuild and scrub verbs, their gRPC RPCs and old MCP tool names raise
LATTICE0002and will be removed in the next major version. (#4122, #4125) (Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Backup9.9.0,Orleans.Lattice.Api.Backup.Grpc9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Admin - Blocking maintenance verbs.
RebuildViewAsync,ReconcileViewAsync,ReconcileTagIndexAsyncandExecuteWalMoveAsyncand their gRPC client methods raiseLATTICE0002and will be removed in the next major version. (#4124) (Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0)
Fixed
Core - Changing a missing tree no longer creates it. A configuration change, shard-map update, split, digest latch or WAL placement change on a never-created or purged tree now fails as not found instead of bringing the tree back. (#4230) (
Orleans.Lattice9.9.0)Core - Key history shows each write once. Copies made by resize, reshard and replication no longer repeat a revision, and the Explorer says "Set - value not kept". (#4149) (
Orleans.Lattice9.9.0)Core - Reads that name no key follow a resize or reshard. Keyless history, projection digests, shard rebuild and compaction, materialiser lag, warm-up, orphaned-leaf audit, WAL placement, state API shard reads, tag-index fingerprint and replication drift walk resolve current routing. (#4146, #4176, #4180) (
Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.State9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Replication9.9.0)Core - Bulk loads, restores and schema cutovers follow a reshard or resize. They resolve the tree's current routing, so keys no longer land on shards no read reaches and reshard-added shards get their redirects and purges. The replication digest probe compares every routed shard. (#4206) (
Orleans.Lattice9.9.0,Orleans.Lattice.Backup9.9.0,Orleans.Lattice.Schema9.9.0,Orleans.Lattice.Replication9.9.0)Core - A shadow-cutover restore or schema cutover of a resharded tree keeps every key readable. The cutover carries the copy's shard map onto the tree, as a resize does, and a revert carries it back. Before, most keys of a restored resharded tree read as absent. (#4250) (
Orleans.Lattice9.9.0,Orleans.Lattice.Backup9.9.0,Orleans.Lattice.Schema9.9.0)Auth - Grants a host seeds at startup are honoured as soon as they are written. A silo whose policy was never written warmed its gate over an empty snapshot and denied requests as "no matching rule" until a slow rebuild. The first rule written now waits for a rebuild that saw it. (#4128) (
Orleans.Lattice.Auth9.9.0)Core - Resize, snapshot and restore no longer freeze while pointing the tree's name at the copy. An alias swap could deadlock the tree registry on a host with apps or an access gate, and a gated resize swap was refused as anonymous on every tick. A stuck swap now finishes when next driven. (#4128) (
Orleans.Lattice9.9.0,Orleans.Lattice.Auth9.9.0,Orleans.Lattice.Membership9.9.0)Schema - Remediation status answers while a run is in progress, and long runs no longer time out. A status read no longer waits for a running remediate or migrate, and a run is driven in resumable slices, so no single cluster call outlasts the response timeout. (#4123) (
Orleans.Lattice.Schema9.9.0)WAL - A GC report field blamed a cause its own predicate excludes.
LatticeWalGcReport.BytePressureOverThresholdclaimed a lagging consumer or pin held bytes back. It is occupancy against the ceiling only, and its pass arm reports only when the trim floor is clear. (#3204) (Orleans.Lattice9.9.0)WAL - A failed durable pin write is retried rather than recorded as done. A batched materialiser pin write recorded its debounce state before writing, so a faulted shard left those pins looking durable and the next report was coalesced away. Each shard is now recorded once its write lands. (#3319) (
Orleans.Lattice9.9.0)Core - A range read no longer comes up short during a leaf division. A donor mid-split hid rows it still held, so a scan, count or stats call silently missed keys a point read still answered, until that leaf's next write, which on a quiet range might never come. (#3918) (
Orleans.Lattice9.9.0)Core - Batch writes can be bounded by a whole-call budget.
SetManyEnvelopeBudgetboundsSetManyAsyncend to end, so stages that each meet the deadline cannot sum past it unseen. The refusal names the per-stage breakdown and covers single-shard batches. Opt-in; unbounded by default. (#2685) (Orleans.Lattice9.9.0)Core - Tombstone compaction no longer times out on a tombstone-heavy leaf. A leaf's reap yields on a work budget and resumes, reaping strictly less each pass until it drains, and a partial pass keeps the leaf queued instead of dropping it. (#4135) (
Orleans.Lattice9.9.0)Core - The cold-replay loop warning now describes what actually happens. It told operators no snapshot is banked and the leaf cannot escape on its own, which stopped being true once a cancelled cold replay began banking its progress. It now names the three cases that still reach the threshold. (#2280) (
Orleans.Lattice9.9.0)Core - A cancelled leaf activation now reports how far its WAL replay got. The applied-entry count was lost when a replay was cancelled mid-slice, and reported only from the deactivation hook Orleans skips when activation throws. It is now kept per entry and reported on failure. (#2411) (
Orleans.Lattice9.9.0)Explorer - A tenant-scoped address lists only that tenant's items.
/t/{tenant}/accessand/t/{tenant}/clusterjoin the other areas; counts, badges, completions and pickers follow. Rule and backup listings takeActiveTenantOnly, narrowed to the caller's validated tenant. (#4025) (Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Auth9.9.0,Orleans.Lattice.Api.Auth.Grpc9.9.0,Orleans.Lattice.Api.Backup9.9.0)Tests - Three fixtures could hang or time out in setup. Two barrier races blocked more pool threads than a cold thread pool holds, and a purge fixture seeded its tree under its own 4 s timeout. The races now run on dedicated threads with bounded joins, and seeding runs outside the timeout. (#4034, #4045, #4032) (
repository-wide)Schema - Unversioned trees were enveloped at version 0. An absent version config read back as a zero-valued config, so writes to a tree with none were wrapped in a (0,0) envelope and the admin reported a phantom config. Absence now reads as null and writes pass through unchanged. (#3993) (
Orleans.Lattice.Schema9.9.0)Schema - Negative max-length rules were accepted. A
MaxByteLengthrule with a negative limit, built without theMaxLengthfactory, passedSetPolicyAsyncand then rejected every value written to the tree. The policy is now refused at set time withArgumentException. (#4097) (Orleans.Lattice.Schema9.9.0)Tenancy - Negative quota ceilings were accepted. A negative
MaxBytes,MaxKeys,MaxMemoryBytesorMaxTreeCountrefused every write the tenant made, and a negativeMaxOpsPerSecondlifted its rate limit. Authoring one now fails withArgumentExceptionand writes nothing. (#4096) (Orleans.Lattice.Tenancy9.9.0,Orleans.Lattice.Api.TenantAdmin9.9.0)Config - Core timer periods above the timer ceiling. A
HotShardSampleInterval,ShardHealingIntervalor viewCoalesceWindowover the grain-timer limit (about 49.7 days) passed validation, then threw at every arming, so sampling, healing or view upkeep never ran. Validation now rejects it. (#4044) (Orleans.Lattice9.9.0)Config - Tenant lease cycle timeout above the timer ceiling. With
LeaseIntervalandLeaseCycleTimeoutboth longer than a timer can wait (about 49.7 days), the rate-budget lease loop died on its first cycle and never apportioned a rate. The timeout now clamps to the ceiling. (#4013) (Orleans.Lattice.Tenancy9.9.0)Config - Grain index backfill interval above the timer ceiling. A
BackfillIntervallonger than a grain timer can wait (about 49.7 days) passed validation, then threw on every arming, so the crawl reported running and never advanced. Validation andWithBackfillIntervalnow reject it. (#4043) (Orleans.Lattice.GrainIndex9.9.0)Config - Grain index names containing a slash. An index named like
users/archivepassed validation, but its registry entries fell inside theusersindex's scan range, so that index's backfill could skip grains. Silo start now rejects a name containing/. (#4273) (Orleans.Lattice.GrainIndex9.9.0)CRDT - Counter component overflow. A G-Counter or PN-Counter advance past
long.MaxValuewrapped the component negative, so the pointwise-max merge discarded it and the write succeeded having counted nothing. It now throwsOverflowExceptionand writes nothing. (#3926) (Orleans.Lattice9.9.0)CRDT - Sequence inserts landed at the wrong index. A replica's
InsertAtAsyncorInsertAfterAsynccould sort after a sibling another replica wrote, so an index-0 insert landed at the tail. A new node now takes a counter above every one observed, so it lands right after its parent. (#4111) (Orleans.Lattice9.9.0)CRDT - A non-positive accessor TTL wrote a durable entry. Eleven CRDT accessor TTL overloads sent a zero or negative
ttlto the no-TTL write, so the entry never expired. Every TTL overload now throwsArgumentOutOfRangeExceptionbefore reading or writing, asILatticedoes. (#4112) (Orleans.Lattice9.9.0)CRDT - Batched OR-Flag enables accepted an empty replica id.
EnableManyAsyncandStageEnableManyAsyncminted dots under a blank id every such writer shares, so one writer's disable could cancel another's concurrent enable. They now throwArgumentException, asEnableAsyncdoes. (#4113) (Orleans.Lattice9.9.0)Observability - Telemetry step overflow. A range query whose step was too large to multiply, on a catalogue entry declaring no step ceiling, threw
OverflowException. The rate window and defaulted span now saturate, so the deployment step guardrail rejects it as a bounds violation. (#3924) (Orleans.Lattice.Api.Telemetry9.9.0)Observability - Storage footprint double-counted across silos. The
tree.storage.bytestelemetry queries and the Overview footprint and cluster-total panels summed a tree's storage series across silos, so a tree two silos export read double its size. They now takemax by (tree). (#4272) (Orleans.Lattice.Api.Telemetry9.9.0,Orleans.Lattice.Dashboards9.9.0)Config - Telemetry request timeout ceiling. A
RequestTimeoutlonger thanHttpClientaccepts (int.MaxValuemilliseconds) passed validation and then failed every resolution of the backend client. Validation now rejects it, naming the ceiling. (#3925) (Orleans.Lattice.Api.Telemetry9.9.0)Replication - The leaf snapshot feed lost expiry and saga state. It rebuilt every exported row as a durable committed value, so a TTL row outlived its lease and an in-flight saga's prepared write or delete surfaced as committed. Rows now keep their expiry, and prepared rows stay prepared. (#3989) (
Orleans.Lattice.Replication9.9.0)Dashboards - Panels filtered on labels their series lack. The Backup scope selector offered no values and blanked most panels, the CommitPath retry-attempts line drew a permanent zero, and the Replication fell-off-log panel never showed data. Each now filters on labels its instrument carries. (#3990) (
Orleans.Lattice.Dashboards9.9.0)Backup - Timings above the timer ceiling. A
CrossTreeFencePollIntervalorSinkSharingProbeTimeoutlonger than a timer can wait (about 49.7 days) passed validation, then failed every cross-tree capture that had to wait, or blocked silo start. Validation now rejects it. (#3967) (Orleans.Lattice.Backup9.9.0)Core - Tree lifecycle follows aliases. Deleting, recovering or purging a resized, restored or remediated tree now acts on its live copy, and a resize no longer reports the tree as deleted when it retires the old copy. Deleting through an alias to a tree it does not own is refused. (#3744) (
Orleans.Lattice9.9.0,Orleans.Lattice.Backup9.9.0,Orleans.Lattice.Schema9.9.0)Observability - Logical tree id after an alias. Mutation observers and the
treemetric tag keep reporting the logical tree id after a resize, restore or schema remediation moves a tree to a new physical copy, so observers, dashboards and alerts keep their series. (#3767, #3780) (Orleans.Lattice9.9.0)Shard - Resize and snapshot dropped split-added shards and resurrected stale values. They handled only the pinned shard count, losing every key on a split shard and serving the donor's pre-split copies. The destination now inherits the routing map and split mark and covers every routed shard. (#3880) (
Orleans.Lattice9.9.0)Shard - Reshard ignored a declared virtual slot count. On an app tree with fewer than 4096 slots, a target above that count passed validation and stayed in progress forever, and an empty-tree reshard rebuilt the map over 4096 slots. Both now honour the tree's slot count. (#3888) (
Orleans.Lattice9.9.0)Container -
-AcceptMultipleDeltascould never be acknowledged.Assert-DeployManifest.ps1assigned a$reasonlocal, which PowerShell resolves to the-Reasonparameter, so an acknowledged step was refused or logged the wrong reason. The local is renamed; a lint rejects the shadowing. (#3598) (repository-wide)Gates - Refinement note left checked properties unmapped.
CommitIntegrityis now mapped andTypeOKdeclared as a reasoned exclusion inspec/Refinement.md, and a gate fails when any property TLC checks is neither mapped nor excluded. (#2558) (repository-wide)WAL - GC reactivation respects the sweep share. The reactivation pass fans leaf touches out within the sweep starvation share and retries a refused touch in the same pass, a refusal no longer spends a reactivation attempt, and the orphan-pin sweep sum is documented as a population bound. (#3761, #2878) (
Orleans.Lattice9.9.0,Orleans.Lattice.Dashboards9.9.0)Leaf - Split and hydration accounting. Recovery-path split completions land on a zero-primed
recoveredoutcome, the bisect-refusal counter documents its corrected cost model, and the hydration heap factor is justified from both the binary and legacy JSON read paths. (#2860, #2856, #2858) (Orleans.Lattice9.9.0,Orleans.Lattice.Dashboards9.9.0)Retrieval - ANN build and load progress misreported. Only a step that banked progress counts as advanced, held and expected vectors and partitions are gauged, a discarded load names its reason, and a response timeout is no longer classified as an unreachable dependency. (#3762, #3761, #3152) (
Orleans.Lattice.Dashboards9.9.0)Observability - Saturation refusals are attributed.
orleans.lattice.saturation.refusalscounts every saturation refusal by tree and source, and a starvation drive refused a replay permit returns a verdict the WAL GC scheduler counts instead of throwing. (#3761) (Orleans.Lattice9.9.0,Orleans.Lattice.Dashboards9.9.0)Observability - Malformed MCP calls are client errors. A tool call failing argument binding or validation gets the same error result, logged at Debug without a stack and counted by
orleans.lattice.api.mcp.tool.client_errors, rather than logged as an unhandled server exception. (#3761) (Orleans.Lattice.Api.Mcp9.9.0)Tests - Replay banking resumes from the banked prefix. The replay-apply-failure fixture now asserts that the next attempt resumes from the banked checkpoint, not only that the checkpoint was banked. (#3092) (
repository-wide)Performance -
performance-report.ps1threw at startup. Its-NamePrefixparameter is declared again, so Layer 1, Layer 2 and self-provisioning Layer 3 sweeps no longer fail under StrictMode, and a gate checks that every documented script parameter is declared. (#3804) (repository-wide)Performance - The azure-throughput rig ignored a documented
0. The silo now honoursBENCH_WAL_APPEND_COALESCING_IN_FLIGHT_THRESHOLD=0(coalescing off) and the TCP producerBENCH_DURATION_SEC=0(run forever) instead of running the default, and a gate checks every documented0. (#3854) (repository-wide)Replication - Same-mode enables from two regions went Ambiguous. Two regions enabling one tree under the same merge mode now converge on that mode instead of leaving the runtime config Ambiguous and receivers dropping the tree's data. Divergent modes still fail closed. (#3899) (
Orleans.Lattice.Replication9.9.0)Tenancy - A dropped region never finished draining. A region removed from residency now advances from Draining to Removed on its own, and a lifecycle promotion can no longer overwrite a later residency change. Promoting an added region to Online is a documented operator step. (#3897) (
Orleans.Lattice.Tenancy9.9.0,Orleans.Lattice.Api.TenantAdmin9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.Mcp9.9.0)Observability - The replay permit gate could not show why it refused. Replay-permit refusals carry an
armtag, permit hold time and service rate are exported, and a no-progress refusal says no permit was released instead of blaming queue depth. (#3921) (Orleans.Lattice9.9.0,Orleans.Lattice.Dashboards9.9.0)Core - A busy resize timed out and could not be undone. The resize snapshot copies in wall-clock-bounded slices, so it no longer outlives the caller's timeout or starves its keepalive. An undo is accepted while a phase runs, unwinds at the next boundary, and shows in resize status. (#3904, #3923) (
Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.Mcp9.9.0)WAL - An undone resize leaked its copy's WAL. Undoing a resize now discards its destination, retiring its materialiser pins and trimming its WAL at once. The WAL GC no longer reactivates a deleted tree's leaves, and it heals copies an earlier undo left behind. (#3930) (
Orleans.Lattice9.9.0)Core - A purge stopped part-way and wedged its tree id. A tree purge is accepted and walked in the background, with shard progress in deletion status, so the response timeout cannot stop it. A tree re-created under a purged id can again be resized, deleted and recovered. (#3940, #3941) (
Orleans.Lattice9.9.0,Orleans.Lattice.Api.Abstractions9.9.0,Orleans.Lattice.Api.TreeAdmin9.9.0,Orleans.Lattice.Api.Mcp9.9.0)WAL - The file WAL acknowledged appends it later discarded. An append that reused an offset at or below the trim watermark was accepted and readable, then dropped by the next recovery as already trimmed. The file provider now rejects it like any other overlap. (#4073) (
Orleans.Lattice.Storage.File9.9.0)Core - Tree admin acted on undefined modes. An unknown
TreeSnapshotModeran an Offline snapshot that quiesced the source, and an unknownTreeHistoryRetentionModecleared the retention override. Both are now rejected withArgumentOutOfRangeExceptionbefore any side effect. (#4075) (Orleans.Lattice.Api.TreeAdmin9.9.0)Core - Tree-admin gRPC client skipped facade guards. A bulk-load operation id containing
/, a negative chunk index, or an empty restore operation id was sent and failed as anRpcException. The client now refuses them with the facade'sArgumentExceptionwithout calling. (#4274) (Orleans.Lattice.Api.TreeAdmin.Grpc9.9.0)WAL - A batch running past the last offset was accepted. Every WAL provider's density check wrapped at
long.MaxValue, so a batch whose offsets ran off the end of the offset space passed and was stored out of order. The file, in-memory and Azure Table providers now reject it. (#4221) (Orleans.Lattice9.9.0,Orleans.Lattice.Storage.File9.9.0,Orleans.Lattice.Storage.AzureTable9.9.0)Storage - A sub-byte capacity threshold disagreed with itself. When a ceiling times the advisory ratio fell below one byte, the threshold truncated to zero: the aggregate read over threshold with nothing retained and the account never did. The threshold is now at least one byte. (#4222) (
Orleans.Lattice.Scaling9.9.0)WAL - GC no longer removes a live leaf's durable pin. The orphan-pin sweep parsed consumer ids with the configured WAL partition count, not the tree's pinned one, so a mismatch could retire a live leaf's pin and trim WAL it had not replayed. Pins not provably a leaf's own are refused. (#4238) (
Orleans.Lattice9.9.0)Core - A purged tree stays purged. No read, background loop or shard seed re-registers a purged tree id, so recover-after-purge refuses, and a purge is not reported complete until the tree's registry entry is gone. (#4219, #4252) (
Orleans.Lattice9.9.0)Core - Bulk load into an empty resharded tree.
BulkLoadAsyncno longer refuses a tree whose shards a reshard, count or warm-up seeded but left empty. A shard that ever held data is still refused. (#4251) (Orleans.Lattice9.9.0)Core - View reads survive a scan reopen. A resilient scan that reopened mid-read dropped its view-read scope, so a view handle read could intermittently hit the protected-view guard. The scope is now kept. (#4186) (
Orleans.Lattice9.9.0)State - Tree config reports the pinned WAL partition count. The tree configuration summary read the configured
WalPartitionsrather than the count the tree's registry entry pins. (#4241) (Orleans.Lattice.Api.State9.9.0)Tests - Timing races in four fixtures made deterministic. Wall-clock interleave assertions, a debounce raced against a real delay, and bUnit reads taken before an async re-render now wait on their condition or drive a manual clock. (#4142, #4243, #4254) (
repository-wide)Tests - Multi-silo fixtures share one WAL and enforce ETags. In-process multi-silo clusters share one in-memory WAL per cluster, test grain storage refuses stale-ETag writes and copies state, and guard tests pin shared storage and one activation per grain id. (#4194, #4196) (
repository-wide)
Security
Replication - Peer statistics recorded trees the receiver dropped. A run for a tree not enrolled here counted as inbound contact, so a peer could plant tree ids in
ReplicationPeerStatsand the peer-status report. Only admitted runs are recorded now, and inbound rows are capped per silo. (#4021) (Orleans.Lattice.Replication9.9.0)Security - A replication mesh secret certified any origin it claimed. Origin binding shipped off by default, so an accepted credential proved only that the caller held some mesh secret, leaving every origin check comparing caller-chosen values. It now defaults on. (#4082) (
Orleans.Lattice.Replication9.9.0)Security - MCP discovery advertised tools the gate would refuse. A key- or prefix-scoped rule on the cluster-wide tree id was read as cluster-wide and carried a scopeless capability, and the per-tool operation filter was skipped absent operation detail. Both now fail closed. (#4082) (
Orleans.Lattice.Api.Mcp9.9.0)Security - A rejected MCP call echoed the caller's key and scope. Any client-error rejection reached the server log and the caller-facing text verbatim and unbounded, so a caller-chosen key could forge a log record with CR/LF. Rejection messages are now sanitized and truncated. (#4056) (
Orleans.Lattice.Api.Mcp9.9.0)Security - Tenant create skipped identity-directory validation. The registered tenant-admin facade was built without the directory, so seeded admin subjects were never checked even with validation required. Create now validates them as adding a subject does. (#4003) (
Orleans.Lattice.Api.TenantAdmin9.9.0)Security - A metric allow-list admitted names it was not written for. Wildcard entries anchored with
$and matched withSingleline, so a caller-supplied name carrying a newline satisfied a deny-all pattern. Entries now anchor with\z, reject newlines, and match without backtracking. (#3929) (Orleans.Lattice.Api.Telemetry9.9.0)Security - A tree-scoped rule granted a scopeless capability. MCP discovery masked only the telemetry bit as scopeless, so an Allow rule on a single tree carried
AppInstallinto the granted operations. Both scopeless capabilities are now carried only from a cluster-wide rule. (#3863) (Orleans.Lattice.Api.Mcp9.9.0)Security - A rejected MCP call echoed the caller's argument names. Unknown argument names reached the rejection message and the server log verbatim and unbounded, so a caller could forge a log record with CR/LF. Names are now sanitized, truncated, and capped in number. (#3972) (
Orleans.Lattice.Api.Mcp9.9.0)