Stratara.Abstractions 4.2.0

Prefix Reserved
dotnet add package Stratara.Abstractions --version 4.2.0
                    
NuGet\Install-Package Stratara.Abstractions -Version 4.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Stratara.Abstractions" Version="4.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Stratara.Abstractions" Version="4.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Stratara.Abstractions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Stratara.Abstractions --version 4.2.0
                    
#r "nuget: Stratara.Abstractions, 4.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Stratara.Abstractions@4.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Stratara.Abstractions&version=4.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Stratara.Abstractions&version=4.2.0
                    
Install as a Cake Tool

Stratara.Abstractions

Derived. The behaviour described here is specified under openspec/specs/. Those specifications are the source; this page explains and illustrates them.

License: MIT.

Contract interfaces and wire-level POCO records for the Stratara framework. Library-safe — depends only on Microsoft.Extensions.DependencyInjection.Abstractions, Microsoft.Extensions.Logging.Abstractions, and Stratara.Contracts. No EF Core or message-bus runtime.

Use this when you need to reference Stratara types without pulling in any concrete implementation (Mediator runtime, EF Core, RabbitMQ, etc.). Typical consumers: handler/projection libraries that ship without a host.

Contents

  • Stratara.Abstractions.MediatorIMediator, IRequest, IRequest<T>, ICommand, ICommand<T>, IQuery<T>, ICommandHandler<T>, IQueryHandler<T,R>, IPipelineBehavior<T>, IPipelineBehavior<T,R>, IAggregateScopedCommand.
  • Stratara.Abstractions.EventSourcingIAggregationService, IEventSource, IEventStreamRepository, ISnapshotRepository, IEvent, IEvent<T>, IAggregateCreationEvent, IChangeSetHandler, EventChainAnchor, EventSubject, ConcurrencyException. Plus wire-types: EventStreamEntry, Snapshot.
  • Stratara.Abstractions.PersistenceIUnitOfWork, IWriteUnitOfWork, IReadUnitOfWork, ITransaction, IDbResolver.
  • Stratara.Abstractions.OutboxICommandOutboxDispatcher, IEventBundleOutboxDispatcher, IOutboxRepository. Plus wire-type OutboxEntry.
  • Stratara.Abstractions.MessagingIMessageBus, IMessagingIdentifier, IEventBusConsumer, IEventBusPublisher.
  • Stratara.Abstractions.SessionISessionContextProvider.
  • Stratara.Abstractions.MultitenancyITenantService, ICurrentUserService, ITenantScopedRequest, ICrossTenantAuthorizer, TenantAccessDeniedException, ITenantMembershipStore. Plus wire-types: TenantMembership, MembershipStatus.
  • Stratara.Abstractions.ProjectionsIProjectionReplayState.
  • Stratara.Abstractions.SecurityIEncryptionFactory, IKeyStore, IMasterKeyProvider, ISecureBlobEncryptor, ISecureJsonSerializer. Plus wire-types: KeyScope, KeyMaterial, EncryptedData, DataSensitivityLevel, EncryptDataAttribute.
  • Stratara.Abstractions.ValidationIValidator<T>, ValidationResult, ValidationFailure, ValidationSeverity, StrataraValidationException.
  • Stratara.Abstractions.EntitiesIEntity, IBucket, IHasRowVersion, IMultiTenant, ITenantEntity, IUserIdentity.
  • Stratara.Abstractions.BackgroundTasksIBackgroundTaskQueue. Plus wire-types: BackgroundTaskInfo, BackgroundTaskStatus.
  • Stratara.Abstractions.CommandsIUpdateCommand.
  • Stratara.Abstractions.AuthorizationRequireRoleAttribute, RequirePermissionAttribute, IAuthorizationProvider, IPermissionResolver, PermissionCatalog, AuthorizationException, PermissionAuthorizationException.
  • Stratara.Abstractions.SettingsISettingProvider, ISettingStore, SettingCatalog. Plus wire-types: SettingDefinition, SettingScope.
  • Stratara.Abstractions.ApiKeysIApiKeyStore. Plus wire-types: ApiKeyDescriptor, ApiKeyIssueRequest, IssuedApiKey.

Why split

NuGet consumers without an event-sourcing host can adopt Stratara's CQRS contracts and authorization model without dragging in EF Core, RabbitMQ, or the WriteStore.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (14)

Showing the top 5 NuGet packages that depend on Stratara.Abstractions:

Package Downloads
Stratara.Sessions

ASP.NET Core middleware and provider for Stratara's Actor/Subject session model. Reads tenant and user from JWT claims (or X-Tenant-Id / X-Client-Id headers) and populates the ambient ISessionContextProvider for every request.

Stratara.Domain

Concrete domain types for the Stratara framework — the Tenant aggregate and its lifecycle events. Stratara's opinionated multitenancy model.

Stratara.Resilience

Polly-based named resilience pipelines pre-wired for the Stratara framework's messaging and command/event dispatch paths. Four named pipelines registered via AddResiliencePipelines().

Stratara.Mediator

In-process mediator with DI-resolved handlers and pipeline behaviors for the Stratara framework. MediatR-style routing via typed wrapper cache, plus an authorizing decorator and a bucket-lock concurrency primitive.

Stratara.Shared

Umbrella package for the Stratara framework — source-generated logger extensions for command flows, domain-event helpers, merge primitives, and the Tier-A/B abstractions surface re-exported as one transitive bundle.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.2.0 27 9/18/2026
4.1.1 244 9/16/2026
4.1.0 196 9/16/2026
4.0.4 807 9/14/2026
4.0.3 397 9/3/2026
4.0.2 803 9/3/2026
4.0.1 383 9/2/2026
4.0.0 810 8/31/2026
4.0.0-preview.1 152 8/31/2026
3.4.0 372 8/28/2026
3.3.0 535 8/25/2026
3.2.3 364 8/22/2026
3.2.2 926 8/14/2026
3.2.1 922 8/2/2026
3.2.0 342 7/18/2026
3.1.7 599 7/1/2026
3.1.6 748 6/22/2026
3.1.5 326 6/22/2026
3.1.4 2,190 6/15/2026
3.1.3 328 6/10/2026
Loading failed

A hardening release for the Orleans execution model, and a new test-support package for it.
`Stratara.Testing.Orleans` runs the execution model inside the test's process. Beyond that, 4.2.0 closes
every gap known before release. A recorded command runs once and in its scope's order, and it is counted
against the bus's two bounds. A store reader paused by a rebuild comes back if its pauser dies, and the
rebuilt read model keeps every fact. A failing saga stops only itself. Every options section the framework
documents is read from configuration, and an anonymous caller is answered 401.

**Upgrading:**
- **Generate an EF Core migration.** The intent record gains a `conflict_count` column.
- **Check `appsettings.json` for `SessionContext:AllowTenantHeader` and
 `Stratara:BlobEncryption:LegacyBlobsCarryPurpose`.** Both were ignored until now and take effect with
 this release.
- **Upgrade the saga silos together.**
- **Start a rebuild only once every silo runs 4.2.0.**

### Added

- **`IProjectionCheckpointStore.FindAsync` and `CreateAsync`**: a checkpoint's position, or `null` where none exists, where
 `GetAsync` answers `0` for a missing checkpoint and one at the beginning alike; and a first checkpoint written only where none exists, never replacing one. The
 framework's store implements both; the defaults throw `NotSupportedException` naming the members, so a store of the
 consumer's own keeps compiling and fails only where a store-reading saga needs them.

- **New package `Stratara.Testing.Orleans`** (test-support, the 28th package): `ExecutionModelTestHost` runs the Orleans
 execution model in a test's own process — one silo, in-memory reminders and grain directory, the real write stack,
 portable commit-order reader, checkpoint and intent stores on in-memory SQLite, and every period shortened to
 seconds. A test registers its roles with the production calls, dispatches, waits for the readers
 (`WaitForReadersAsync`) and asserts; `Timers`, `SeedAtHeadAsync`, `ResetAsync` and `ExecutionModelTestHostOptions`
 (with `BeforeStart`) complete it. The package carries the `STRATARA1001` build check and refuses a stated non-development
 environment.
- **Sample `Stratara.Sample.OrleansExecutionModel`**: a command in its aggregate's activation, a projection from the
 store and a process timeout on the test host, in one console run; smoke-tested.
- **`AddStrataraTestingEventStore<TWriteDbContext>(connectionString, tenantId, configureContext)`**: a connection per
 context to a shared database, and the caller's options — an interceptor — after the provider.

- **`AddCommandServices()`** (`Stratara.EventSourcing.WorkerDefaults`): the command worker stack without the bus-fed
 mediator worker, beside `AddEventProjectionServices` and `AddSagaServices`. `AddCommandWorkerServices` is now
 `AddCommandServices` plus the worker. A silo composed with it, the execution model's command dispatcher and intent
 store, the aggregate grains and a store-reading role runs commands and applies their facts without a message broker,
 as does an API host that only dispatches through the execution model's dispatcher.

- **Orleans: `AddStrataraSingletonWork<TWork>(string name, ...)` and `OutboxDrainWork.WorkName`.** A work registered
 with its name is published in the silo's metadata without being constructed, so it is first constructed when the
 silo is active; a work whose `Name` differs from the registered name fails the start naming both.
- **Orleans: log event `117_119`** (`LogEvents.Orleans.SingletonWorkFailed`) for a run of a singleton work that threw;
 the work runs again at its next period, as before. **`117_120`** (`LogEvents.Orleans.RolesUnpublished`) for a silo
 that does not start because it hosts roles or work it does not publish.

- **Orleans: a recorded command is signed and verified.** Where the host registered a bus-envelope signer, the record
 the execution model writes before a dispatch returns carries the signature a bus command carries, and the drain
 verifies it under `BusEnvelopeIntegrityOptions.Mode` before resuming it: under `Strict` an unsigned or invalid record
 is kept at once with the reason, under `Permissive` it is resumed and logged. Log events `117_115`–`117_118`.
- **`ICommandIntentStore.ClaimAsync`** claims a batch of due commands; the default claims row by row through
 `TryClaimAsync`, the shipped store in two statements.
- **Orleans: log event `117_008`** (`LogEvents.Orleans.HandlerStoppedWithSilo`) for a handler on a grain path cancelled
 because its silo stopped.
- **`IProjectionCheckpointStore.AdvanceAsync`** advances a checkpoint from the position its writer last saw. The
 default replaces the position through `SetAsync`, so a store of the consumer's own keeps compiling; the shipped
 store refuses a write that finds another position or another reader.
- **Orleans: log events `117_005`–`117_007`** — `StoreReaderRetired` for a reader beyond the host's partition
 count, `ResumeHeldBackByReplay` and `ResumeReleasedAfterReplay` once each when a full replay holds recorded
 commands back and releases them.
- **Orleans: a host seeds its store readers at the head.** `IStoreReaderSeeding.SeedAtHeadAsync`, registered
 with `AddStrataraProjectionGrains` and `AddStrataraSagaGrains`, writes a checkpoint at the store's current
 head for every registered projection and saga and partition that has none, and reports how many it wrote and
 how many existed. A host whose read models are current runs it once, while no silo runs, before its first
 start — without it the first start re-applied the whole history. A consumer registered later still starts
 at the beginning.
- **`ICommittedPositionReader.HeadAsync`** reports a partition's head — the position after which nothing
 committed exists — with a default that walks the partition; both shipped readers answer in one query.
- **Orleans: `CommitTransactionIdBackfill`** stamps the commit record on a populated PostgreSQL event table in
 append order and bounded batches, so the native reader's column is added without rewriting the table and
 without the whole history landing under one transaction id.
- **Orleans: `AddStrataraExecutionModelReset` takes the schema the runtime tables live in.** `schema`
 defaults to `public`; a reminder or membership table absent under it fails the reset naming the table
 instead of reporting that nothing was removed, and the three runtime tables are cleared in one transaction.
- **Orleans: two log events.** `117_112` (`LogEvents.Orleans.IntentAttemptFailed`) for every attempt of a
 recorded command that fails, with the command's id, type and aggregate; `117_113` (`HandOverFailed`) for a
 hand-over that fails. A failing handler is seen before its command is kept.
- **Orleans: log event `117_114`** (`LogEvents.Orleans.PermitReclaimRefused`) for a running heavy unit that was
 refused its permit when it registered again after the permit keeper was lost, and runs outside the
 cluster-wide bound until a permit is free.
- **`SessionRequiredException`** (`Stratara.Abstractions.Session`): the failure of an operation that must be
 attributed to a caller and runs with no session context — a save, a command audit, a dispatch on the bus path and
 on the execution model's path. It derives from `InvalidOperationException` and keeps the message these operations
 always used, so an existing `catch (InvalidOperationException)` and a log search on the text keep working; a host
 catches it without referencing any store or broker package.

- **`ICommandIntentStore`: `RecordAsync(…, recordedAt, …)`, `TryRenewAsync`, `TryRenewFromAsync`,
 `RecordConflictAsync` and `ReturnAttemptAsync`**, each with a default that does what a store written against 4.1
 did — the record's time is the store's own and its count starts at zero, every hand-over runs, a conflict is
 recorded as a failure, a stop gives nothing back.
 `OutboxEntry.ConflictCount` and `RecordedIntent.ConflictCount` (defaulted) carry the conflict count.

### Changed

- **Orleans: each store-reading saga reads with a checkpoint of its own.** A saga's reader is keyed and checkpointed
 under `sagas:<SagaName>` — the saga's type name — instead of the consumer `sagas` all sagas of a deployment shared,
 so `orleans.reader.stalled`, the stall log `117_101` and the attempt log `117_102` carry the failing saga's consumer
 name; a dashboard or alert that filters on `sagas` must be widened to the `sagas:` consumers. Seeding, the
 execution-model reset and the wake-up after a commit name every saga's consumer, and the reset also removes the
 shared `sagas` checkpoint of 4.1.x. A saga without a checkpoint starts where the deployment's sagas read in that
 partition — the shared checkpoint of 4.1.x, or else the furthest checkpoint another saga holds — so a saga added
 to a running deployment runs no side effect for the store's history. *Upgrade note:* nothing is migrated; on the
 first start every saga starts at the shared checkpoint, and the shared reader, brought back by its 4.1.x keep-alive,
 retires and logs `117_125` (information). While a 4.1.x saga silo runs beside a 4.2.0 one both apply the facts
 above the shared checkpoint, so upgrade the saga silos together. A rollback to 4.1.x resumes the shared reader
 from the shared checkpoint, which 4.2.0 never advanced, and applies what the per-saga readers applied since
 again. Seeding a store with the shared checkpoint starts each saga there rather than at the head, so the shared
 reader's backlog is not skipped. A host that registers two saga types of the same type name — in different
 namespaces — no longer starts, naming both, because they would share one checkpoint; rename one before upgrading.
 A saga's reader brought back on a silo that registers no saga of its name — a removed or renamed saga — retires,
 unregisters its keep-alive and logs `117_126` (information) instead of stalling on every entry. A host with a
 checkpoint store of its own must implement `IProjectionCheckpointStore.FindAsync` and `CreateAsync` (see *Added*)
 to run store-reading sagas.

- **Security: `"SessionContext": { "AllowTenantHeader": true }` in the host's configuration now turns the tenant-header
 fallback on.** `AddSessionContext()` did not read the `SessionContext` section, so such an entry did nothing unless
 the host bound the section itself; it is read now (see *Fixed*). Before upgrading, search every environment's
 configuration for `AllowTenantHeader` and remove the entry or set it to `false` unless the host means to accept the
 `X-Tenant-Id` header. Likewise, a `Stratara:BlobEncryption:LegacyBlobsCarryPurpose` entry now takes effect for a host
 that registers the blob encryptor through `AddSecurity()` or `AddStrataraBlobEncryption()` alone.
- **`AddStrataraProblemDetails()` answers a caller that is not authenticated with 401.** An authorization refusal, a
 tenant-access denial and a `SessionRequiredException` alike: what such a caller lacks is an identity, not a
 permission. Where the host has a default authentication scheme the mapping challenges through it — a bearer client
 receives `WWW-Authenticate`, a cookie client is redirected, as `[Authorize]` would do — and writes the problem body
 where the challenge leaves a 401; without a scheme it writes a 401 problem response. **A client that treated 403 as
 "log in" now receives 401**, which is the status that says so. An authenticated caller's denial stays 403, and a
 `SessionRequiredException` for an authenticated caller is not converted — it means the host did not run the
 session middleware. A host that does not register the mapping sees nothing new but the exception type.
- **Orleans: `hybrid: true` without a bus dispatcher fails at registration** naming `AddOutboxDispatcher`; it ran
 grain-only without a word.
- **The execution model's operating limits are documented**: a forwarded command whose handler outlasts the response
 timeout fails its caller and commits (do not retry on a timeout); a resumed command is authorized from its recorded
 session, so the authorization provider answers from the session, not the web request; the record of a command is
 committed on its own, not with the caller's writes; a full replay applies the store twice to store-reading
 projections; singleton work may overlap on two silos for one membership refresh after a death declaration, and the
 failover latency follows from the membership settings. `ISingletonWork` and `IAuthorizationProvider` say so in their
 XML documentation.
- **Orleans: a silo places grains on itself by the roles and work it publishes, before its own metadata reaches its
 cache.** A silo that became active and started its store readers at once could fail its start with *No silo of the
 cluster registered the projections role*, because its own metadata was not yet known to it.
- **Orleans: a wake-up nudge that cannot be sent no longer fails the dispatch after a commit.** A commit made before the
 silo started, or while it stopped, threw from the bundle dispatcher although the facts were stored; the nudge is now
 lost like any other and the readers' poll applies the facts.
- **The portable commit-order reader is verified on SQLite** as well as PostgreSQL, through the test host.
- **Migration guide: the command silo and the bus queues.** The command role's row names `AddCommandServices`, the
 dispatcher and the intent store; *When the broker can go* states which hosts need the broker; *After the cut-over:
 the bus queues* names the queues the bus workers own and the order in which to retire them.
- **Orleans: every registration of the execution model is idempotent.** A second call of
 `AddStrataraProjectionGrains`, `AddStrataraSagaGrains` or `AddStrataraSingletonWork<TWork>` registered its wake-up
 target or its work again, so every projection was woken twice per bundle and the seeding and reset listed each
 consumer twice.
- **Orleans: a silo that hosts a role or singleton work without publishing it fails at start.** A silo that registered
 the grain directory under the model's name itself rather than with `AddStrataraOrleans` started and was placed on as
 if it hosted every role; it now fails naming the roles and works it found and `AddStrataraOrleans`. A silo that
 hosts nothing placed by role is unaffected.
- **Orleans: the drain resumes a backlog as fast as the handlers take it.** While a pass finds a full batch due, the
 next pass follows at once, for at most `OutboxDrainOptions.PollingInterval`; a backlog was resumed one batch per
 period.
- **Orleans: a stopping silo tells the handlers on its grain paths.** Command handlers in an aggregate's activation or
 a runner, heavy work and timer handlers receive a `CancellationToken` that is cancelled once the silo has been
 stopping for `GrainCollectionOptions.DeactivationTimeout`; it was `CancellationToken.None`. A recorded command
 stopped this way is resumed elsewhere with no attempt counted, a forwarded command fails back with a message saying
 the silo stopped, and a timer stays registered and fires on the next silo.
- **Orleans: a timer registered from its own handler is kept.** A handler that registered its owner and purpose again
 with the same due time lost the new timer when its tick unregistered itself.
- **Orleans: `IDurableTimers` refuses an owner id longer than the reminder table holds** (139 characters) on every
 member, with a message naming the limit, instead of failing at the first tick.
- **Orleans: the heavy-work bound holds across the loss of the silo keeping the permits.** A permit keeper
 that is activated admits no new heavy unit for one `PermitLease` and takes back every running unit that
 registers again, so the units the lost keeper had admitted count against the bound before new ones start;
 previously a new keeper handed out the whole bound at once beside them. A running unit whose permit was lost
 now reads the answer when it registers again and keeps asking until it holds a permit. The first heavy
 commands after a keeper's activation — including a cluster's first — start one lease later.
- **The migration guide says how a populated store adopts the execution model.** The transaction-id column
 is added nullable, backfilled, then given its default and constraint — while nothing appends; the guide
 states the upgrade order (schema before the first 4.1 host, 4.0 hosts keep running, bus outbox worker
 before the first drain silo, seed, silos, API host, bus workers, queues), that a checkpoint is keyed by the
 projection's simple class name, and the prerequisites of the Orleans packages.
- **Orleans: every role is placed on the silos that registered it.** A silo publishes the roles its
 composition registers — commands with `AddStrataraAggregateGrains`, projections with
 `AddStrataraProjectionGrains`, sagas with `AddStrataraSagaGrains`, timers with `AddStrataraDurableTimers`
 and an `ITimerOwners` — and aggregates, command runners, heavy-work pools, projection, saga, process and
 timer-owner grains are placed only on silos publishing their role, as singleton work already was. Roles
 may be split across silos; a call for a role no silo registered fails naming the role and the registration.
 The heavy-work pool is no longer a stateless worker on the calling silo: it is one pool per eight permits
 of `HeavyWorkOptions.ClusterWideLimit`, placed on silos of the command role. An API host that only
 dispatches may join as an Orleans client.
- **Orleans: a send that would close a cycle between aggregates is refused at once.** A handler running for
 aggregate A that sends to B, whose handler sends back to A, used to wait for the runtime's response
 timeout; the send back is now refused with a message naming both aggregates, and both commands fail at
 once. Sends between aggregates form a directed acyclic graph.
- **Orleans: a read that fails counts as a stall.** A store that cannot be read, or a checkpoint the reader
 refuses, is logged as `117_103` and counted in `orleans.reader.stalled` whichever wake-up or poll started
 the read, not only on a nudge.
- **Orleans: the portable commit-order reader states its preconditions.** It is verified on PostgreSQL,
 and through the test host on SQLite; every process that appends to a store it reads needs `PartitionCounterInterceptor` — the framework
 does not add it, and `CommitOrderOptions.MaintainPartitionCounter` is only the value a write context reads
 when it does; and the partition count cannot change once the store holds positions. On PostgreSQL the
 native reader remains the one to use.
- **`PartitionCounterBackfill` positions a batch with one statement on PostgreSQL** instead of one per entry;
 other providers keep the per-entry update, and the positions it hands out are the same.

- **Orleans: a recorded command is counted the bus's way — schema change.** The record counts the hand-over of the
 dispatch as the first of `MessageRetryOptions.MaxDeliveryAttempts` when it is written, so a handler that keeps
 failing runs as often as the bound says (before, once more), and a host that dies before that hand-over has used
 the attempt, as a crashed delivery does on the bus — under a bound of 1 such a command is kept for an operator
 without running. A concurrency conflict — `ConcurrencyException`, what the bus transports count as one — gives its
 attempt back and counts against `MessageRetryOptions.MaxConflictRequeues` instead (before, against the delivery
 bound of 3). A stop gives its attempt back, for the running handler and for the commands queued behind it. The
 count lives in the new column `outbox_entry.conflict_count`: generate and apply an EF Core migration before the
 first 4.2 host starts, as for 4.1 (see the migration guide). A command recorded under 4.1 carries no counted first
 attempt and runs as 4.1 ran it. An operator returning a kept command also resets `conflict_count` and
 `last_failure`.

### Deprecated

- **`CommitOrderOptions.MaintainPartitionCounter`** is obsolete: the framework never read it. A write context that
 maintains the partition counter adds `PartitionCounterInterceptor`. The member is removed with the next major.

### Fixed

- **Orleans: a resumed command runs once however its resumptions meet.** Two claimers stamping the same millisecond
 — the singleton drain during a failover, a bus outbox worker beside the drain during adoption — both handed the
 command over, and a command naming no aggregate, or one whose first run had ended, ran twice; so did a hand-over
 arriving after the command completed. The hand-over now carries the claim's stamp and runs only if its receiver
 takes the record over from that stamp; one that finds it moved or the record gone is dropped and logged as
 `117_124` (`IntentHandOverDropped`, Debug).
- **Orleans: commands one scope dispatches to one aggregate are resumed in dispatch order.** The record's time was
 taken after the payload was serialized, so a slower first record resumed after the second — `SetPrice 10` after
 `SetPrice 20`. It is now taken when the dispatch starts, strictly increasing per aggregate within the scope, and due
 commands are resumed by that time, then by id.
- **Orleans: a silo that stops no longer takes an attempt from the commands it was running or had queued.** The
 stopped handler, and every recorded command queued behind it that the stop gives up, gives back its attempt, so
 rolling deploys during a long handler no longer keep commands for an operator.
- **Orleans: a hand-over the fence dropped no longer holds its command.** An aggregate's activation refused a later,
 valid hand-over of the same command while the dropped one waited in its queue.
- **Orleans: a dispatch's hand-over that arrives after a resumption completed the command is dropped.** A hand-over
 arriving later than a sixth of the grace checks that its command is still recorded and not kept.
- **`Stratara.Testing.Orleans`: a recorded command is resumed on the test host.** Its SQLite write store kept points
 in time as text, which the intent store's due query cannot compare; they are kept as numbers now.
- **Orleans: a recorded command's time comes from the registered `TimeProvider`.** It was the wall clock while the
 drain compared it with the registered clock, so a host or test with its own clock saw commands never become due, or
 become due at once.
- **Orleans: a failing saga no longer repeats its siblings or stops them.** All store-reading sagas of a partition
 shared one reader and one checkpoint, so an entry one saga failed on was retried for every saga on each poll and
 wake-up, without a bound — a saga that had succeeded on it, sending an email or issuing a command, did so again
 every few seconds until the failing one was fixed — and every later fact of the partition waited for it. A saga
 that fails now stops only its own reading of the partition; every other saga applies the entry once and goes on.

- **An anonymous caller reaching an unguarded save or dispatch is no longer a server error.** The event source, the
 command audit, the bus dispatcher and the execution model's dispatcher threw a plain `InvalidOperationException`
 that the problem-details mapping let through as a 500 reading like a framework bug; they now throw
 `SessionRequiredException`, which the mapping answers 401 for a caller that is not authenticated.
 `RequireAuthorization()` on every endpoint is no longer needed to avoid that.
- **Orleans: a singleton work's settings apply to that work alone.** The callback given to
 `AddStrataraSingletonWork<TWork>(configure)` and `AddStrataraSingletonWork<TWork>(name, configure)` configured one
 settings object shared by every work, so two works registered with keep-alive periods of two and five minutes both
 ran with whichever callback ran last. Each work now runs with the host's `SingletonWorkOptions` — what
 `services.Configure<SingletonWorkOptions>(...)` sets — with its own callback applied on top, and a work registered
 twice runs with the later registration's callback, once, rather than with both. Each work's settings are validated
 when the host starts. **A host that relied on one work's callback to configure the others** sets the value for all
 of them with `services.Configure<SingletonWorkOptions>(...)` instead; a host that already configures
 `SingletonWorkOptions` directly is unaffected.
- **Every options type that names a configuration section is read from it.** `SessionContextOptions` (`SessionContext`),
 `ProjectionReplayOptions` (`ProjectionReplay`) and — for a host without `AddStrataraFileKeyStore` —
 `StrataraBlobEncryptionOptions` (`Stratara:BlobEncryption`) were registered and bound by nothing, so a value in the
 documented section did nothing. `AddSessionContext()`, `AddProjectionReplayState()` and `AddStrataraBlobEncryption()`
 now read the section from the host's configuration, directly or through `AddOutboxDispatcher()`, `AddSecurity()` and
 the worker composites. A service collection without an `IConfiguration` keeps the defaults; a value configured in
 code after the registration takes precedence, and calling a registration again does not re-apply the section over
 it. `AddStrataraOrleansCommandDispatcher()` likewise reads `MessageRetryOptions` (`MessageRetry`) and validates it at
 start when no bus transport is registered before it — a host whose commands run only through the execution model
 set the resume bound in that section to no effect; where a transport registered first already reads the section, it
 stays the only reading. A test now holds every public options type with a section name to being read from it.
- **A projection replay lease of zero or less is refused at start.** `ProjectionReplayOptions.LeaseSeconds <= 0` fails
 the host with an `OptionsValidationException` naming `ProjectionReplay:LeaseSeconds`; it was accepted, and on the
 in-process replay state the marking lapsed at once and publication resumed in the middle of a rebuild.
- **Orleans: `hybrid: true` keeps a bus dispatcher registered by factory or as an instance.** `AddStrataraProjectionGrains`
 and `AddStrataraSagaGrains` kept publishing bundles to the bus only when the dispatcher had been registered by type; a
 factory- or instance-registered one was removed and bundles stopped reaching the bus.
- **Orleans: `PartitionCounterBackfill` no longer renumbers positioned entries.** Unpositioned entries take the
 positions after the partition's counter, so a checkpoint written before a backfill stays true; previously the
 backfill shifted every positioned entry and a resumed reader re-applied one entry and lost another.
- **Orleans: the portable reader stops at its partition's unpositioned entry however many other partitions hold.**
 The probe was cut at 64 unordered rows of any partition.
- **Orleans: positions within one save follow each stream's version order**, by contract rather than by the change
 tracker's enumeration.
- **Orleans: a store reader applies each entry under its own tenant.** A projection, a saga and the services they
 depend on are resolved after the entry's session is set — one scope per run of entries recorded under one
 session — so a dependency that takes the tenant when constructed no longer sees the first entry's tenant, or
 none, for a whole read. A process reads its state under the session of the fact it is handed.
- **Orleans: a checkpoint is no longer rewound by a stale activation**, nor written under another reader's name;
 the refused reader reads the checkpoint again. `SetAsync` of the shipped store refuses a row held by another
 reader.
- **Orleans: a reader of a partition beyond a lowered partition count retires** instead of returning every
 keep-alive period to be refused and counted as stalled.
- **Orleans: an aggregate's order runs to the end however long it takes.** The grain ran its accepted
 commands through an ordinary call to itself, which the runtime timed out after `MessagingOptions.ResponseTimeout`
 (thirty seconds); the grain read the timeout as "the run never started" and failed every command still
 waiting back to its caller, dropping the leases of the recorded ones. The call is one-way now; the
 response timeout bounds a caller's wait for one forwarded command only, which the operations guide says.
- **Orleans: a running command is renewed whether or not its handler yields.** The lease and permit
 renewals ran on the activation's scheduler, so a heavy handler that computed without awaiting past
 `IntentGrace` was never renewed, was claimed by the drain and ran a second time. Both renewals run from
 timers of their own.
- **Orleans: a heavy hand-over is leased while it waits for a worker.** A burst that queued units for
 longer than the grace handed the queued ones over twice; the pool accepts a hand-over at once and its
 lease covers the wait for a slot and a permit. A hand-over the pool already holds is not accepted twice.
- **Orleans: two rebuilds of one projection no longer interleave.** The pause was a flag: the first rebuild's
 resume let the readers re-read and advance their checkpoints before the second rebuild truncated, which
 left the read model empty for good. Pauses are counted, the readers resume when the last pauser resumes,
 and a rebuild requested during a full replay is refused naming the replay.
- **Orleans: the reset is resolved from a scope in the documentation and its example.** It is a scoped
 service; resolving it from the root provider throws wherever scope validation is on.
- **Orleans: a batch cut short by the reader's shutdown still records the checkpoint for the entries it applied.**

…the complete notes for this release are in the changelog: https://github.com/yesbert/Stratara/blob/v4.2.0/CHANGELOG.md