Skip to content

deps: Bump WolverineFx and 4 others - #148

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/src/Infrastructure/wolverine-7fe0d09bbb
Open

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/src/Infrastructure/wolverine-7fe0d09bbb

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 26, 2026

Copy link
Copy Markdown
Contributor

Updated WolverineFx from 6.46.0 to 6.54.0.

Release notes

Sourced from WolverineFx's releases.

6.54.0

Highlights

Dead letter failure groups (#​5209, #​5210). Wolverine now fingerprints every message as it moves to dead letter storage, so dead letters can be managed one failure group at a time:

  • The default IDeadLetterFingerprinter hashes the exception type and the first stack frame from application code. Register your own to replace it.
  • DeadLetterEnvelopeQuery gains FailureGroup, ExceptionMessageMatch (Exact / Contains), TenantId and Archived.
  • A new SummarizeAllAsync overload groups by failure group, message type or endpoint, with first and last failure times.
  • ReplayMatchingAsync, DiscardMatchingAsync, ArchiveMatchingAsync and RestoreMatchingAsync report how many dead letters they affected.
  • A message that fails again after a replay reports its ReplayCount, LastReplayedAt and FirstFailedAt.
  • Archived dead letters drop out of summaries, queries and replays until restored, and can be expired separately with DurabilitySettings.ArchivedDeadLetterRetention.
  • IDeadLetters.Capabilities tells you what a store supports.

All changes are additive. The relational stores pick up seven nullable columns on wolverine_dead_letters, an index and a new wolverine_dead_letter_history table through the normal schema migration. See the dead letter storage guide.

Deduplication fixes (#​5205, #​5206, #​5208), and Bobcat 1.0.0-rc.4 dependency pins (#​5211). The WolverineFx.Bobcat* packages remain alpha (6.54.0-alpha.1).

What's Changed

Full Changelog: JasperFx/wolverine@V6.53.0...V6.54.0

6.53.0

What's Changed

New Contributors

Full Changelog: JasperFx/wolverine@V6.52.0...V6.53.0

6.52.0

Wolverine 6.52.0 is a feature release. The headline items are Aspire-style registration by connection-string name for every transport and every message store, timer agents for short-cadence recurring schedules, and the 6.x groundwork for Wolverine 7.0's one-chain-per-handler model. There are no breaking API changes. Two behavior changes are worth knowing about up front: recurring schedules that fire more often than every 15 minutes now run on a timer agent (opt out per schedule with UseScheduledMessages()), and outgoing envelopes a transport cannot map are dead-lettered instead of being logged and deleted.

Many thanks to everyone who reported issues and sent fixes: @​uniquelau, @​johnkaram02, @​erdtsieck, @​ahmetvayhan and @​pritamrao.

Aspire parity: named connections

Messaging and error handling

HTTP

Operability and diagnostics

Documentation

Full Changelog: JasperFx/wolverine@V6.51.3...V6.52.0

6.51.3

Wolverine 6.51.3 is a durability release. Nearly everything in it came out of a run of reports against the durable inbox and outbox, plus a new durable receiver compliance rig (src/Testing/DurableComplianceRig, Bobcat specs across PostgreSQL, SQL Server, Marten and EF Core over local, database, RabbitMQ and SQS endpoints) that turned up the rest. There are no breaking API changes; the one schema addition (a nullable envelope_id column on the deduplication table) is optional and an unmigrated table keeps working.

Many thanks to everyone who reported these with reproductions: @​billshuck, @​dbelinsky-accureference, @​endava-marinela-marchitan, @​devployment, @​kowalczb, @​Patrick2607 and @​ahmetvayhan.

Durable inbox hardening

Durable bug fixes

Testing

Full Changelog: JasperFx/wolverine@V6.51.2...V6.51.3

6.51.2

Bug fix release on the JasperFx 2.91.0 / Marten 9.51.0 / Polecat 5.43.0 / Fisher 1.21.2 matched set, plus the Bobcat specification work

What's Changed

Committed directly to main

  • GH-5067 event-model --url stamps the pushed Event Model with its checkout root, repository and branch (dded67e95)
  • GH-5078 AgentAssignmentsChanged is obsolete, and the test observer's dead branch for it is removed (c1b22941a)

Full Changelog: JasperFx/wolverine@v6.51.1...V6.51.2

6.51.1

Bug fix release, and some doc updates too

What's Changed

Full Changelog: JasperFx/wolverine@V6.51.0...v6.51.1

6.51.0

Everything since 6.50.1.

Highlights

  • Warm-up nodes for blue/green deployments (#​4569, #​5024). Set opts.Durability.NodeRole = NodeRole.Warmup and a node joins the serving cluster but does none of the serving nodes' messaging. Its only job is to run the new release's projection shards in Rebuild mode, so the new projection versions are caught up before you cut over. The role is opt-in; the default is NodeRole.Serving, and no schema change is needed. Docs: Marten · Polecat
  • DurabilityMode.ServerlessWithOutbox (#​5016, #​5044). The lean Serverless runtime, but with the transactional outbox kept:
    • Outgoing messages are persisted with the handler's transaction.
    • They are sent inline before the request completes.
    • Anything that couldn't be sent is recovered by a durability pass.
    • Follow-ups are in #​5058 (#​5046–#​5057), #​5060 (#​5059) and #​5065 (#​5038).
    • Docs: Serverless with the Transactional Outbox
  • Wolverine.HTTP JSON options (#​5042, #​5064). HTTP handlers mapped directly now use ASP.NET Core's JsonOptions without needing MapWolverineEndpoints(). Two new properties, HttpChain.JsonSerializerOptions and HttpHandler.JsonSerializerOptions, give a single endpoint its own JSON options.
  • Failure log level (#​5043, #​5064). Set it with chain.FailureLogLevel, opts.Policies.MessageFailureLogLevel(...) or [WolverineLogging(FailureLogLevel = ...)]. The default is still Error.

Durability fixes

  • Durable database queue pops (#​5015, #​5020 by @​jcd1991, #​5021). A failed or lost pop no longer strands envelopes on a live node, on PostgreSQL, SQL Server, MySQL and Oracle. Thanks to @​dbelinsky-accureference for the report.
  • InboxStaleTime (#​5022, #​5025, #​5039):
    • Its clock now measures the time since a node claimed the message, not since the message was first written.
    • It leaves scheduled rows alone.
    • A node keeps the clock running for the messages it still holds, so a long handler is no longer started a second time.
  • Projection daemon self-healing (#​4986 by @​erdtsieck). A shard the daemon paused on a transient failure, such as connection pool exhaustion, is restarted with a capped backoff instead of staying paused until the pods restart.
  • Serverless startup with RavenDB (#​5030, #​5041 by @​jcd1991). Plain Serverless mode no longer fails at startup with RavenDB persistence.
  • Messages replayed on every restart (#​5040, #​5045, #​5062). A handled message is now marked handled in the store that holds its inbox row. This affected two setups:
    • a durable PostgreSQL queue with a DbContext enrolled in an ancillary store
    • Fisher or Polecat with an AddMessageHandler handler committing an ancillary session
  • PostgreSQL transport schema (#​5062). The transport takes its storage schema from the store it is bound to, not from the last PostgreSQL store registered.
  • More #​5038 follow-ups (#​5065):
    • Outside ServerlessWithOutbox, a failed inline send is retried by the destination's sending agent.
    • RavenDB and Cosmos DB take one durability pass lock per database or container.
    • EF Core and RavenDB write the same outgoing owner id as the other stores.
    • Plain Serverless warns about durable endpoints downgraded after startup.

Dependencies

  • 6.50.2 (#​5018): Weasel 9.45.1, Polecat 5.40.2 and Marten 9.50.2, for the System.Security.Cryptography.Pkcs advisory GHSA-846f-f764-76mx.
  • 6.51.0 (#​5063): JasperFx 2.90.1, Marten 9.50.3, Polecat 5.42.0 and Fisher 1.21.1.

6.50.2

A dependency patch release. No configuration or code changes needed.

Dependencies

This moves Wolverine onto the Critter Stack releases that fix CVE-2026-84008 / GHSA-846f-f764-76mx (System.Security.Cryptography.Pkcs, high-severity denial of service) (#​5018):

  • Weasel 9.45.1: Weasel.SqlServer and Weasel.Oracle now reference the patched Pkcs directly.
  • Polecat 5.40.2 (up from 5.38.0): Polecat now references the patched Pkcs directly. This also brings in Polecat 5.39 and 5.40.
  • Marten 9.50.2 (up from 9.49.0) for Marten, Marten.AspNetCore and Marten.Newtonsoft. Marten was never affected by the advisory.

Wolverine 6.50.1 already pinned the patched Pkcs in WolverineFx.SqlServer, WolverineFx.Oracle and WolverineFx.ClaimCheck.SqlServer, so applications on 6.50.1 were already protected. Those pins stay, because Wolverine references Microsoft.Data.SqlClient and Oracle.ManagedDataAccess.Core directly. 6.50.2 means the upstream libraries carry the fix too, so a NuGet audit comes back clean whichever of them your application references directly.

6.50.1

Patch release with a memory-leak fix and two security dependency updates.

Fixes

  • WolverineRuntime now disposes its Meter (#​5012, #​5013). The runtime's System.Diagnostics.Metrics.Meter used to stay in the process-wide static Meter registry after the host was disposed. The durable persistence gauges on it hold callbacks back into the runtime, so every disposed host stayed reachable for the life of the process. Test suites that build many WebApplicationFactory or Host instances with durable persistence saw the heap grow without bound. Thanks to @​AndriiKononenko3 for the detailed report and the retention path.

Security dependency updates

  • CVE-2026-84008 / GHSA-846f-f764-76mx (System.Security.Cryptography.Pkcs, high-severity denial of service). The package arrived transitively at vulnerable versions through Microsoft.Data.SqlClient, Oracle.ManagedDataAccess.Core and Polecat. WolverineFx.SqlServer, WolverineFx.Oracle and WolverineFx.ClaimCheck.SqlServer now reference the patched version directly: 9.0.21 on net9.0 and 10.0.13 on net10.0. Every package built on them, including WolverineFx.Polecat and WolverineFx.Http.Polecat, picks up the fix.
  • CVE-2026-70159 / GHSA-334f-vvp4-7g6p (Microsoft.AspNetCore.SignalR.Common, high-severity denial of service). WolverineFx.SignalR now depends on Microsoft.AspNetCore.SignalR.Client 9.0.21 on net9.0 and 10.0.13 on net10.0, up from 9.0.8.

6.50.0

Wolverine 6.50.0 is a feature release. The headline is a new way to send a message to every node in your cluster. Just as much of this release came from the community, though: five community pull requests landed, and several issue reports led straight to fixes.

The two Bobcat packages, WolverineFx.Bobcat and WolverineFx.Bobcat.Http, ship as 6.50.0-alpha.1 and are still pre-release.

Broadcast a message to every node in the cluster

  • BroadcastToAllNodesAsync() (#​4993, closes #​4988). Wolverine's normal delivery is competing consumers, where exactly one node handles a message. Sometimes you want every node to hear about something instead: a cache to invalidate, a configuration change to pick up, some node-local state to reset. Now you can just do this:

    await bus.BroadcastToAllNodesAsync(new InvalidateCache("products"));

    Every running node, including the one that sent it, handles the message exactly once with its own handler. The sending node gets the message on the local routes its type already has, or on local://default, and it's never forwarded to the external routes PublishAsync() would use. Every other node gets a copy on its node control endpoint, which is the broker control queue if you've configured one and the database control queue otherwise. Nodes that have gone stale are skipped, using the same rule the leader applies before ejecting a node. A broadcast from inside a handler goes through the outbox like any other outgoing message, and correlation ids are stamped as usual. In Solo and Serverless durability modes the message is delivered to this node only, and MediatorOnly refuses it just like PublishAsync(). It's an extension method on IMessageBus, so no other IMessageBus implementation breaks, and TestMessageContext records broadcasts in a new BroadcastToAllNodes list for your unit tests. See the new Broadcast Messages to Every Node guide.

From the community

A big thank you to everyone who sent pull requests and reports for this release:

  • @​jcd1991 contributed two PostgreSQL transport features:

    • Opt-in LISTEN/NOTIFY wakeups for PostgreSQL queues (#​4998, closes #​3480). With EnableQueueNotifications(), an idle queue listener is woken as soon as a message is written instead of waiting out its polling interval. In local benchmarks the median delivery time on an idle queue dropped from about 650ms to under 3ms. It's off by default because it holds one extra, long-lived database connection per database per running process. Read the trade-offs in the new Queue Notifications with LISTEN/NOTIFY section before you turn it on.
    • FIFO ordering for sharded PostgreSQL queue batches (#​4970, closes #​4862, reported by @​alexandrefresnais). Messages for one group id that went through a PostgreSQL shard queue could run out of order. EnableDeterministicQueueOrdering() now preserves their order, and it was followed up for SQL Server, MySQL, Oracle and SQLite in #​4987.
  • @​jcd1991 also fixed Wolverine.HTTP treating a container-registered service as the JSON request body on POST and PUT endpoints (#​5010, closes #​3863, reported by @​erdtsieck). The same parameter on a GET endpoint was correctly injected, but on a POST or PUT every call failed with a 500. An explicitly registered, non-keyed concrete type is now resolved from the container on every endpoint. This is a behavior change: if you register a type in the container and post it as a request body without [FromBody], add [FromBody] to keep binding it from the request.

  • @​erdtsieck found and fixed a ping-pong in agent assignment during blue/green warm-ups (#​5000, closes #​4999). Projection partitions that could only run on the previous version's nodes moved between those nodes on every evaluation, which showed up in production as 1,000 to 3,000 ReassignAgent commands a minute. A partition that fits nowhere now stays where it is unless another node is strictly lighter. The follow-up in #​5001 makes it settle in one move.

  • @​PascalEschbach contributed a big round of NATS follow-ups (#​4976, closes #​4973), with tests for every change:

    • one schedule subject per message, with listeners that skip schedule control messages;
    • resources setup provisioning declared streams once per connection;
    • per-tenant connection configuration, tenant subject mappers, and a new TrustTenantIdHeader setting;
    • safer dead letter forwarding that only terminates the original after a confirmed forward;
    • a deadline on every JetStream publish;
    • named consumers for dotted subjects;
    • NATS logging through the host's ILoggerFactory;
    • a health check that reports a failed listener start.

    #​4985 followed up on the core side.

  • @​Maflend reported that the static handler registry ignored custom discovery rules once conventional discovery was disabled (#​4984), a regression since 6.44.0. It's fixed in #​4992.

  • @​uniquelau's report in marten#​5652 led to #​5011, below: the leader no longer builds every event store's full usage descriptor on every heartbeat.

Agents and event subscriptions

  • The leader's heartbeat no longer builds the full event store descriptor (#​5011, closes #​5002). Assigning event subscription agents only needs each store's async shard names and its database topology. Wolverine now reads those through JasperFx 2.87.1's IEventStore.RegisteredShardNames() and the new IEventStore.TryDescribeDatabasesAsync(), instead of a full TryCreateUsage() that also read the highest event sequence and described every projection, subscription and event type, for every store, every 10 seconds. To get the full saving, also take Marten 9.50.1, Polecat 5.40.1 or Fisher 1.20.2, which answer TryDescribeDatabasesAsync() straight from their tenancy.
  • A projection rebuild always hands its shard back to continuous execution (#​5008, closes #​5003). A rebuild that threw used to leave the shard stopped, for good if it had already paused on a transient failure. The registered-projection rebuild path that CritterWatch uses never resumed the shard at all. Both paths now resume it whether the rebuild succeeded or failed. The health-check sweep also leaves a shard alone while its projection is being rebuilt, using JasperFx's new IProjectionDaemon.IsRebuilding(), so it can no longer start a second copy of the shard on top of a running rebuild.

Transports and persistence

  • SQL Server queue tables can switch between the default layout and OptimizeQueueThroughput() (#​4991, closes #​4989).
  • EnableDeterministicQueueOrdering() for SQL Server, MySQL, Oracle and SQLite (#​4987).

HTTP

  • Wolverine.Http binds single-Value strong-typed id wrappers, such as record struct OrderId(Guid Value), without a TryParse (#​4995, closes #​4978).
    ... (truncated)

6.49.0

Wolverine 6.49.0 is a feature release. The big themes are optimized envelope ids for SQL Server, additive per-message topic routing across NATS, RabbitMQ and MQTT, a long list of NATS hardening, and a big round of Event Modeling and Bobcat work. Thank you to @​knotekbr, @​me-azlam-kp, @​faustodc, @​luisarellano09 and @​alesdvorakcz, whose reports, tests and pull requests are all over this one.

The two Bobcat packages, WolverineFx.Bobcat and WolverineFx.Bobcat.Http, ship as 6.49.0-alpha.3 and are still pre-release.

SQL Server

  • Optimized envelope ids for SQL Server, automatically (#​4967, closes #​4902, analysis and measurements by @​alesdvorakcz). Wolverine's default envelope ids sort in time order the way .NET and PostgreSQL compare GUIDs, but SQL Server compares a uniqueidentifier starting from its last six bytes, so to SQL Server those ids arrive in random order and fragment the clustered inbox, outbox and dead letter tables. One production inbox was measured at 99% fragmented with its pages half full. The new EnvelopeIdGeneration.NewIdSqlServerOrder uses the same NewId algorithm with the timestamp where SQL Server sorts on it. Wolverine now switches to it on its own at startup when every message store in the application is SQL Server -- plain SQL Server persistence, the SQL Server transport, EF Core on SQL Server, or Polecat -- and you haven't set EnvelopeIdGeneration yourself. There's no schema change. If a SQL Server-only application sends messages to Wolverine services that store their envelopes in PostgreSQL, opt out, because the receiving service uses the same id as its inbox key and PostgreSQL indexes these ids worse than random GUIDs. Either set opts.EnvelopeIdGeneration = EnvelopeIdGeneration.NewId or use the new optimizeEnvelopeIdsForSqlServer: false overloads of PersistMessagesWithSqlServer(), UseSqlServerPersistenceAndTransport() and Polecat's IntegrateWithWolverine(). Behind it, IMessageStore gains a PreferredEnvelopeIdGeneration member with a default implementation, so existing stores are unaffected.

Routing

  • Publish to a broker topic and to a local queue at the same time (#​4960 and #​4966, superseding #​4930 from @​luisarellano09). A per-message topic rule -- PublishMessagesToNatsSubject<T>(), PublishMessagesToRabbitMqExchange<T>() or PublishMessagesToMqttTopic<T>() -- used to silently switch off an explicit PublishMessage<T>().ToLocalQueue() rule, local routing to a handler, and broker conventions for the same message type. Each of those now has an overload that takes additive: true, so the other routing rules still apply. The original overloads are unchanged. A custom IMessageRouteSource can opt into the same behavior through the new IPassThroughMessageRouteSource, and ExplainRoutingFor() labels such a source [additive, pass-through].
  • MultipleHandlerBehavior.Separated for conventional routing on Amazon SQS, Azure Service Bus queues and GCP Pub/Sub (#​4948, #​4949, #​4951, closing #​4524, #​4525 and #​4526). Each handler type gets its own queue or subscription, on top of a small new hook for routing conventions.

NATS

  • An inline endpoint throws a broker-refused publish to the caller (#​4958, closes #​4955). A JetStream publish the server refuses -- a stream full under DiscardPolicy.New, a failed Nats-Expected-* check, an oversized message -- used to be retried three times, logged as errors, and dropped, while SendAsync() returned as if it worked. Now it isn't retried, and SendAsync() throws the new SendRejectedException with the JetStream error inside. Other transports can throw SendRejectedException from their senders to get the same treatment.
  • Resource setup creates a declared stream from its declaration (#​4957, closes #​4956). A missing stream that was declared with DefineStream() used to be created as a default work-queue stream, which then showed up as drift under Provisioning(Verify).
  • Leftovers from the 6.47 JetStream hardening (#​4945, #​4860): the transport reuses a live connection across start retries; header values are sanitized of CR/LF; stream mirrors, sources and placement can be declared; and JetStream publish options are configurable through ConfigureJetStreamPublish.

HTTP

  • A request body Kestrel refuses keeps Kestrel's status code (#​4934 by @​knotekbr, closes #​4933). A body over [RequestSizeLimit], for example, now answers 413 instead of a 500 saying the body could not be deserialized.
  • An optional body sent chunked is no longer silently dropped (#​4936 by @​knotekbr, closes #​4935), followed by exact parity with Minimal API for optional bodies (#​4944) and Kestrel-backed tests for request bodies (#​4942), since TestServer and Kestrel don't agree on the details.

Persistence and durability

  • Conjoined EF Core multi-tenancy with partition-per-tenant (#​4937, closes #​3531, #​3541 and #​3542). EF Core sagas support partition-per-tenant, there's a rebuild API for turning on PartitionPerTenant for a table that already exists, and EF Core, Marten and Polecat conjoined tenancy are tested together in one database. Along the way, this fixed a saga saved with a temporary EF Core key landing under a tenant id that was a random GUID.
  • A handled message is never brought back to life by a late retry (#​4950, the main remaining work on #​4216). Every message store now leaves a handled inbox row alone when a retry is booked for it after the fact. Before, most stores turned it back into a scheduled message that would run a second time. RavenDB and CosmosDB also got an update-first reschedule and a fixed release query.
  • CosmosDB under MessageIdentity.IdAndDestination (#​4969, closes #​4216). Running the shared message store tests under that identity found that it put slashes into Cosmos document ids, which Cosmos does not allow, so every update quietly missed its document. That's fixed, and CosmosDB and RavenDB both now run the whole contract under that identity.
  • The database control listener stops without waiting out its poll delay (#​4952 by @​me-azlam-kp), which took up to five seconds off a quiet node's shutdown on the relational stores and CosmosDB.
  • A composite listener keeps disposing its children after one fails (#​4953 by @​faustodc).
  • Fixed the CosmosDB CI hang (#​4913) by moving to Microsoft.Azure.Cosmos 3.63.2 and pinning the emulator image.

Agents and leadership

  • Leadership election and agent assignment follow-ups (#​4932). A database split across nodes is consolidated onto the node running most of it, and pins are respected. A dead node's queued starts are abandoned instead of stalling. There's a cold-start hold, and a new leadership eligibility setting. Both new DurabilitySettings have backward-compatible defaults.

Code generation

  • Generated code no longer depends on what else the process planned first (#​4946, closes #​4877). The local variable a continuation frame declares used to be numbered from a process-wide static counter.

Event Modeling and Bobcat

  • A new Event Modeling tutorial (#​4963, closes #​4918) walks the whole Critter Stack workflow in one place: declaring the model in code, importing a board with Bobcat, the generated specifications, and scaffolding.
  • wolverine scaffold follows Bobcat's Features/{Chapter} layout (#​4959), gains --json output with stable notice codes (#​4947, closes #​4928), and has a round of output fixes: record views built with Create, the events that start a stream first, and long attributes on their own line (#​4929). Scaffolded HTTP endpoints are tested against Marten, Polecat and Fisher, and a stream start answers 201 with its id (#​4947, closes #​4927).
  • The CritterCrush review (#​4924, closing 15 issues) covered the event-model scaffold, [Emits] inference, definition discovery, describe-event-model, and new WolverineFx.Bobcat assertions.
  • Tracked sessions record the events each session appended (#​4943, closes #​4931, #​4865, #​4919 and #​4926), along with WolverineFx.Bobcat support for DCB tags, paragraphs and FetchLatest read models, and DCB and multi-stream scaffolding.
  • Bobcat specifies a DCB decision's consistency boundary (#​4964, refs #​4940), checked deterministically.
  • ThenEventuallyCaused checks the events an act caused past an async boundary (#​4965, closes #​4939). The new WolverineFx.Bobcat step finds them by correlation id once the async daemons catch up, and there's an opt-in correlation header for every HTTP endpoint.

6.48.3

A patch release for blue/green warm-ups at fleet scale, from a second production report by @​erdtsieck on 6.48.2: every green-only agent went to the first green node to register, and the two green nodes that joined seconds later got nothing for half an hour.

  • A warm-up spreads over every green node that joins (#​4904, @​erdtsieck). Four causes, all in the leader: the pending-start ledger was re-applied over the whole grid before every family's pass, so the passes after the event-subscription family put each still-queued agent back on the first green node and the decision to move it never left the leader; a move of a start that was still only queued waited in the source node's lane behind that node's whole backlog, and now goes straight to the new node; the group-affinity fallback had no per-node ceiling; and a move of an agent whose first move had not landed could start it on a third node, so an outstanding move is now left alone until it lands.
  • Three gaps in that new machinery, found by a randomized soak on real dispatcher lanes (#​4909). A start queued for a node that had since left no longer steals the claim of a newer start elsewhere; a queued re-drive of a start whose assignment row has not surfaced yet is no longer withdrawn by a later move as if it had never run; and a pending move that goes unconfirmed past the ledger's TTL during a long start — the field's own shape, 27-second starts against a 20-second TTL — is re-driven as the stop-then-start it was, never as a bare start that skips the stop at a source whose copy is still coming up.
  • Fleet-scale simulated scenarios (#​4905, @​erdtsieck). A full blue/green deploy, a green node dying mid-wave, the leader dying mid-warm-up, starts failing against some databases, slow starts on every node, and blue pods that captured different tenant sets, replayed through the simulated cluster with real dispatcher lanes; a production-sized shape (512 databases, ~100,000 agents) is opt-in with WOLVERINE_FLEET_SCALE=production.
  • A chain that only reads through IQuerySession but sends messages now gets its commit (#​4910, @​srimalw; #​4912 for Polecat and Fisher). The IQuerySession permutation of #​2941: the read-only parameter was served by casting the outbox-enrolled session, but CanApply ignored it, so a scheduled cascade was never handled and a durable local message only ever ran from memory. Reading alone still does not make a chain transactional; reading plus sending does — an injected IMessageBus, a returned OutgoingMessages, or a cascading return value.

Everything else is as in 6.48.2.

6.48.2

A patch release for agent assignment at fleet scale, from a production report by @​erdtsieck: ~62,000 per-tenant event-subscription agents on 5 to 8 nodes, where a newly elected leader spent 66 minutes in its first assignment evaluation during a blue/green warm-up and three leaders in a row were replaced before one finished.

  • Matching agents to capable nodes goes through a hash set instead of a list scan (#​4886, @​erdtsieck). AssignmentGrid.MatchAgentsToCapableNodesFor was quadratic in the agent count whenever any capability differed between nodes — which a single bumped projection version is enough to trigger.
  • The rest of that first evaluation is no longer quadratic either (#​4893). Four more scans of the same shape on the same path: batchCommands removing each start command from the list one at a time, the heartbeat Fill()-ing every running agent into the node record, per-placement Fill() on the grid's node, and per-placement recounts in both even distribution paths. Measured through the leader's real evaluation at the reported shape: the even blue/green path's first evaluation went from 76 s to under 5 s.
  • A newly elected leader holds unplaced agents for one evaluation after a takeover (#​4899). The pending-assignment ledger is leader-local, so a leader replacing one that died with starts in flight could not see them and could start an agent a second time on another node. New DurabilitySettings.LeaderTakeoverHoldEvaluations (default 1, 0 disables) withholds bare first-time placements for that many evaluations after election, so the predecessor's starts can surface as assignment rows and be kept where they are. It stands down when no other node holds any assignment, so a cold start pays nothing.
  • Lifecycle and soak coverage for assignment (#​4893, #​4896). A simulated cluster drives the leader's real evaluation through whole lifecycles — a full blue/green deploy, a leader replaced or killed mid-wave, scale-down, repeated scale-up/down — asserting no double start, no cross-fleet placement and bounded convergence at every step, plus an opt-in randomized soak (WOLVERINE_CHAOS_SOAK=<rounds>) that stays out of CI.

Everything else is as in 6.48.1.

6.48.1

A patch release for one global partitioning bug found by @​alexandrefresnais right after 6.48.0.

  • A global partition slot's backlog now resumes on its new owner in the order it was sent (#​4863). When a slot moved, the losing node's un-executed companion backlog was released to the inbox (GH-4777) and recovered by the new owner (GH-4776), but the recovery enqueued it in the page query's arbitrary order, so messages sharing a group id ran out of sequence on the gaining node. Inbox recovery now accumulates a sweep's pages and hands them to the listener ordered by the envelope's sent time, with the id as tie-break. Provider-agnostic, no schema or query change.

Everything else is as in 6.48.0.

6.48.0

A fast follow-up to 6.47.0 with five fixes, three of them for how a Wolverine cluster behaves when nodes come and go.

Clustering and agent distribution

  • A leader could silently lose the ability to command a peer after five quiet minutes (#​4868, #​4869). The idle sending-agent cleanup disposed the sender to a peer's control queue, and the cached message route kept handing requests to the disposed agent, where a completed block dropped them without a trace. Node agent commands (StartAgents, StopAgents, QueryAgentPresence) were logged as enqueued, never reached the broker, and timed out, so agents stopped on a scaled-in node were never re-placed. Routes now resolve their sending agent on every use. Reported from production on Azure Container Apps with Azure Service Bus control queues; the workaround there was a very long SendingAgentIdleTimeout.
  • A global partition slot moved away from a node whose listener was paused by back pressure kept running on both nodes (#​4866, #​4872). The stop path returned early when the listener had already been taken down by back pressure, skipping the companion-queue drain from GH-4777 and leaving the status TooBusy, so the back pressure sweep later restarted the slot on a node that no longer owned it. Reported with a complete reproduction by @​alexandrefresnais.
  • An operator's restart or pin now reports when the agent did not start (#​4871). ApplyRestrictionsAsync waits for the released or pinned agent to appear in the persisted node assignments and throws AgentRestrictionsNotConfirmedException naming it, instead of returning as if the agent had started. New DurabilitySettings.AgentRestrictionConfirmationTimeout (default 10 seconds) bounds the wait. This is what lets CritterWatch stop acknowledging a restart that started nothing.

RabbitMQ

  • One listener failing to rebuild after a connection recovery no longer strands every listener behind it (#​4864, #​4867, by @​tannerwatson). Each agent is rebuilt independently, failures are retried on a bounded schedule, and channel teardown tolerates a channel the client already closed or disposed. Seen in production across a 3-node RabbitMQ cluster roll, where 15 of 47 pods were left with listeners at zero consumers while health checks stayed green.

Deduplication

  • [Deduplicated] takes its own claim window (#​4857, #​4858, by @​uniquelau). [Deduplicated(WindowInSeconds = 600)] gives one handler a different claim lifetime from the host-wide DeduplicationWindow, matching what [DeduplicatedWithResponse] already offered for HTTP. Works across the RDBMS, Marten, Polecat and Fisher deduplicators; pre-generated code for handlers without a window is unchanged.

Thanks to @​alexandrefresnais, @​tannerwatson and @​uniquelau for the reports, reproductions and pull requests.

6.47.0

Wolverine 6.47.0 is a release that largely came from outside the core team. The biggest change in it, a hardening pass over the NATS JetStream transport, was reported, designed and written by a community member, and so was the fix for a global partitioning handoff that stranded scheduled messages. Thank you to everyone who sent code, reproductions, or diagnoses this time around.

Community contributions

  • @​PascalEschbach reported eight distinct problems in the NATS transport on #​4845, three of them silent message losses, and then fixed every one of them in #​4856. The report alone was unusually good: each item came with the mechanism, not just the symptom, and the PR arrived with a test per item that fails without its fix. Details under NATS below.
  • @​BlackChepo found and fixed the case where a scheduled message parked under a global partition slot was promoted by the node that had just given the slot up, which threw into a stopped listener and committed the rows under a live node so recovery never touched them again (#​4823, GH-4822).
  • @​smoqmilus reported, reproduced and diagnosed two losses of a forwarded scheduled envelope (GH-4824), and is credited as a co-author on the fix (#​4830).
  • @​chrisbbe added the Marten-backed Native AOT smoke lane (#​4826) that exposed three startup failures no native image in CI had ever reached, and is credited as co-author on the direct construction of the internal agent routers (#​4842).

NATS JetStream

Sending durably over JetStream, with an outbox in front of a stream, lost messages in several places without an error or a log line. All of them are closed (#​4856, with follow-ups in #​4859):

  • A publish the server refuses now fails the send. NATS answers a refused publish (a full stream under DiscardPolicy.New, a failed Nats-Expected-* check, an oversized message) with a PubAck carrying an error, not an exception. Wolverine read only the sequence, reported success, and the durable outbox deleted a message the stream never stored. It throws now, the outbox keeps the message, and a duplicate Nats-Msg-Id is still a success.
  • A message Wolverine moves to the error queue is dead-lettered on Wolverine's decision, not only once JetStream's MaxDeliver is used up. A buffered listener acknowledges on receipt, so under the old guard the message was simply gone. The dead letter copy carries its own Nats-Msg-Id, because the original's id had it discarded as a duplicate when the dead letter subject sat in the same stream.
  • Sharded subject streams use work-queue retention. AsWorkQueue() has always meant interest retention, which drops a message published while no consumer is bound, which is exactly the startup and rebalancing window a sharded topology has. The streams UseShardedNatsSubjects() declares now keep a message until it is acknowledged. AsWorkQueue() itself is unchanged; its docs now say what it does.
  • Dropped core NATS messages are logged. A full subscription channel silently dropped messages; the loss is now a warning, throttled per subscription.
  • ConfigureNatsOpts() gives you the last word over the NATS.Net client options, for the pending channel size, ping interval and anything else Wolverine does not surface. It applies to the shared and every tenant connection.
  • Listeners reconcile their named consumers on start, so changing AckWait or MaxDeliver in code just works. Provisioning(NatsProvisioning.CreateOnly | CreateOrUpdate | Verify) controls what startup does with declared streams and named consumers that already exist, with Verify failing the start and listing every deviation. Resource setup and the listener now write the same consumer settings, filter included, so a Verify host starts cleanly after resource setup.
  • Resource setup honors the JetStream domain, so a leaf node no longer creates streams in its own JetStream instead of the configured domain. Only a 404 counts as "missing" anywhere Wolverine looks a stream or consumer up; any other failure propagates with its real message.
  • InvokeAsync() fails at once when nobody is listening. A request now carries a per-request reply subject, so the server's "no responders" answer reaches the waiting call and it fails in milliseconds with WolverineRequestReplyException instead of waiting out its timeout. The responding side answers on that exact subject, which a NATS user limited to allow_responses requires.

Global partitioning and durability

An exploratory pass over global partitioning after the long chain of slot handoff fixes, looking for the next "runs on a node that does not own the slot" before it was reported (#​4853). It found two:

  • A slot handoff releases the backlog that came through the shard queue. Every earlier regression fixture published from a node that owned the slot, so every inbox row carried the companion address. In a real cluster that is the minority path: a non-owner's message goes into the shard queue, and the owner's pop wrote the inbox row at the slot's address, which the handoff release did not cover. There is now a real multi-node Balanced fixture for this path.
  • The shard queue bridge reports its depth, so the back pressure it applies is visible.

And around it:

  • Scheduled messages parked under a slot are promoted on the node that owns it now, not the one that just gave it up (#​4823).
  • A forwarded scheduled envelope is stored as outgoing before its inbox row is retired, which closes two losses: a poll by the owning node between the send and the delete, and a failed first send (#​4830).
  • Ejecting a stale node, the step that releases every envelope the dead node owned, was the one link in crash recovery with no log line. It logs a warning now, and each sub-threshold stale observation on the way to it at debug (#​4854).
  • A requeue from an external durable listener no longer double-counts Envelope.Attempts; a database or broker queue saw attempts go 1 then 3 (#​4855).

Native AOT

The 6.46.0 AOT work stopped at the first failure each publish cycle, because the smoke target did. That is fixed, and the lanes it now runs found the rest:

  • Every Native AOT smoke lane runs and all the failures are reported together (#​4839). The literal explanation in the 6.46 bug reports was "each one appears once the previous one is worked around".
  • Marten- and Polecat-backed native lanes (#​4826, #​4828). The first native image in CI to boot a store-backed HTTP application found three startup failures: SideEffectPolicy could not find Execute on IStartStream, the HTTP IEndpointMetadataProvider close was trimmed, and Marten could not map Envelope in a native image. All three are rooted or avoided now.
  • The message routers are de-genericized (#​4849). A handler returning a value type, the ordinary InvokeAsync<Guid> request/reply shape, killed a native image at startup because the routing warm-up closed a generic over it reflectively. The routers take the message type as an argument now, so the one reflective close every message type had to be rooted for is gone, along with everything that existed to prop it up.
  • The framework's own agent messages are constructed directly (#​4842). They are internal, so generated code could never name them in a rooting block, and an application with a durable message store trimmed exactly those instantiations.
  • The side-effect roots match the test the policy actually applies, the HTTP rooting order is guarded, and a Balanced native lane runs in CI (#​4847).

Also in this release

  • WolverineFx.InMemory is a new package that runs Wolverine over the JasperFx.Events.InMemory prototyping store, so a store-agnostic application's handlers run before anyone has chosen Marten, Polecat or Fisher. Only Wolverine's store-agnostic usage is supported (#​4844).

Commits viewable in compare view.

Updated WolverineFx.EntityFrameworkCore from 6.46.0 to 6.54.0.

Release notes

Sourced from WolverineFx.EntityFrameworkCore's releases.

6.54.0

Highlights

Dead letter failure groups (#​5209, #​5210). Wolverine now fingerprints every message as it moves to dead letter storage, so dead letters can be managed one failure group at a time:

  • The default IDeadLetterFingerprinter hashes the exception type and the first stack frame from application code. Register your own to replace it.
  • DeadLetterEnvelopeQuery gains FailureGroup, ExceptionMessageMatch (Exact / Contains), TenantId and Archived.
  • A new SummarizeAllAsync overload groups by failure group, message type or endpoint, with first and last failure times.
  • ReplayMatchingAsync, DiscardMatchingAsync, ArchiveMatchingAsync and RestoreMatchingAsync report how many dead letters they affected.
  • A message that fails again after a replay reports its ReplayCount, LastReplayedAt and FirstFailedAt.
  • Archived dead letters drop out of summaries, queries and replays until restored, and can be expired separately with DurabilitySettings.ArchivedDeadLetterRetention.
  • IDeadLetters.Capabilities tells you what a store supports.

All changes are additive. The relational stores pick up seven nullable columns on wolverine_dead_letters, an index and a new wolverine_dead_letter_history table through the normal schema migration. See the dead letter storage guide.

Deduplication fixes (#​5205, #​5206, #​5208), and Bobcat 1.0.0-rc.4 dependency pins (#​5211). The WolverineFx.Bobcat* packages remain alpha (6.54.0-alpha.1).

What's Changed

Full Changelog: JasperFx/wolverine@V6.53.0...V6.54.0

6.53.0

What's Changed

New Contributors

Full Changelog: JasperFx/wolverine@V6.52.0...V6.53.0

6.52.0

Wolverine 6.52.0 is a feature release. The headline items are Aspire-style registration by connection-string name for every transport and every message store, timer agents for short-cadence recurring schedules, and the 6.x groundwork for Wolverine 7.0's one-chain-per-handler model. There are no breaking API changes. Two behavior changes are worth knowing about up front: recurring schedules that fire more often than every 15 minutes now run on a timer agent (opt out per schedule with UseScheduledMessages()), and outgoing envelopes a transport cannot map are dead-lettered instead of being logged and deleted.

Many thanks to everyone who reported issues and sent fixes: @​uniquelau, @​johnkaram02, @​erdtsieck, @​ahmetvayhan and @​pritamrao.

Aspire parity: named connections

Messaging and error handling

HTTP

Operability and diagnostics

Documentation

Full Changelog: JasperFx/wolverine@V6.51.3...V6.52.0

6.51.3

Wolverine 6.51.3 is a durability release. Nearly everything in it came out of a run of reports against the durable inbox and outbox, plus a new durable receiver compliance rig (src/Testing/DurableComplianceRig, Bobcat specs across PostgreSQL, SQL Server, Marten and EF Core over local, database, RabbitMQ and SQS endpoints) that turned up the rest. There are no breaking API changes; the one schema addition (a nullable envelope_id column on the deduplication table) is optional and an unmigrated table keeps working.

Many thanks to everyone who reported these with reproductions: @​billshuck, @​dbelinsky-accureference, @​endava-marinela-marchitan, @​devployment, @​kowalczb, @​Patrick2607 and @​ahmetvayhan.

Durable inbox hardening

Durable bug fixes

Description has been truncated

Bumps WolverineFx from 6.46.0 to 6.54.0
Bumps WolverineFx.EntityFrameworkCore from 6.46.0 to 6.54.0
Bumps WolverineFx.Postgresql from 6.46.0 to 6.54.0
Bumps WolverineFx.RabbitMQ from 6.46.0 to 6.54.0
Bumps WolverineFx.RuntimeCompilation from 6.46.0 to 6.54.0

---
updated-dependencies:
- dependency-name: WolverineFx
  dependency-version: 6.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: wolverine
- dependency-name: WolverineFx.EntityFrameworkCore
  dependency-version: 6.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: wolverine
- dependency-name: WolverineFx.Postgresql
  dependency-version: 6.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: wolverine
- dependency-name: WolverineFx.RabbitMQ
  dependency-version: 6.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: wolverine
- dependency-name: WolverineFx.RuntimeCompilation
  dependency-version: 6.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: wolverine
- dependency-name: WolverineFx.RuntimeCompilation
  dependency-version: 6.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: wolverine
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Type: dependency updates (deps) labels Oct 26, 2026
@github-actions github-actions Bot added backend Area: .NET backend and its tests worker Area: the job worker host labels Oct 26, 2026
@github-actions

Copy link
Copy Markdown

Test results

Suite Result Passed Failed Skipped Total Duration
Backend (Debug) ✅ 1,633 0 0 1,633 2m 50s
Backend (Release) ✅ 1,633 0 0 1,633 1m 42s
Frontend (Vitest) ✅ 399 0 0 399 42s
E2E (Playwright) ✅ 68 0 0 68 56s

Coverage

Backend — Line 93.6% (9,961 of 10,631 lines, −0.1 vs main) · Branch 82.7% · Method 95.4%

Frontend — Line 89.5% (1,080 of 1,206 lines, ±0.0 vs main) · Branch 83.6% · Method 88.2%

Backend coverage per assembly (unit + integration merged)
Assembly Line Branch Method
AiFramework.Api 94.6% 77.4% 96.1%
AiFramework.Application 93.8% 85.5% 95.2%
AiFramework.Domain 96.4% 100.0% 90.9%
AiFramework.Infrastructure 94.4% 83.2% 96.6%
AiFramework.PartnerSimulator 76.9% 45.4% 88.2%
AiFramework.Worker 90.0% 66.6% 88.8%

Commit 165b7f2 · workflow run · TRX, JSON and Cobertura files are attached to the run as artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Area: .NET backend and its tests dependencies Type: dependency updates (deps) .NET Pull requests that update .NET code worker Area: the job worker host

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants