A Big Week for the Critter Stack: Marten 9.3, Wolverine 6.2, Polecat 4.2 β
The Critter Stack 2026 majors went stable just over a week ago, and the post-GA cadence has been intense. Between May 22 and May 29, we shipped 8 Marten releases, 4 Polecat releases, and 4 Wolverine releases β a mix of feature drops, performance work, source-generator polish, and a steady stream of real-adopter bug fixes. Here's a tour of what's new.
Release Timeline β
| Day | Marten | Polecat | Wolverine |
|---|---|---|---|
| May 23 | β | 4.1.0 | β |
| May 24 | 9.0.1 | 4.1.1 | β |
| May 25 | 9.0.2 | β | β |
| May 27 | 9.2.0 | 4.2.0 | β |
| May 28 | 9.2.1, 9.3.0 | 4.2.1 | 6.1.0, 6.2.0 |
| May 29 | 9.3.1, 9.3.2, 9.3.3 | β | 6.2.1, 6.2.2 |
The patch storm at the end is mostly the natural shape of GA adoption β real users surfaced subtle bugs, fixes landed within hours, and the major-version branches have settled rapidly.
Marten 9.3.0 β Binary Events, PostGIS, and pgvector β
This is the biggest feature release of the week. Three sizable additions all land in one ship:
π Binary Event Serialization β
Opt individual event types into a binary wire format on a per-event-type basis β MemoryPack out of the box, or bring your own IEventBinarySerializer. JSON-serialized and binary-serialized events coexist in the same mt_events table, so the feature can be turned on in an existing system with no data migration.
opts.Events.UseBinarySerialization(b =>
{
b.MapBinaryEvent<OrderPlaced>(); // opt in by event type
});Works on every EventAppendMode (Rich, Quick, QuickWithServerTimestamps) and through BulkEventAppender. There's a new optional NuGet, Marten.MemoryPack, for the MemoryPack-backed implementation.
π Binary Event Serialization docs Β· π Versioned-event-types schema-evolution recommendation
π Marten.PostGIS β Spatial Queries β
A new opt-in package wires up PostGIS, NetTopologySuite, and GeoJSON serialization, then exposes four geometry-aware query helpers:
options.UsePostGIS();
// On any IQuerySession:
var nearest = await session.NearestToAsync<Cafe>(location, take: 10);
var withinRadius = await session.WithinDistanceAsync<Cafe>(location, meters: 500);
var containing = await session.ContainingAsync<Cafe>(point);
var crossing = await session.IntersectingAsync<Cafe>(polygon);UsePostGIS() enables the postgis extension on every database Marten manages β multi-tenant aware.
π PostGIS docs
π Marten.PgVector β Vector Similarity Search β
Companion package for embedding-based search:
options.UsePgVector();
// Persist embeddingsβ¦
public class VectorProjection<TDoc> { /* base class with embedding hooks */ }
// β¦then search:
var results = await session.VectorSearchAsync<Document>(queryEmbedding, topK: 25);Also addresses #2515 β extensions are now installed inside tenant databases under conjoined and per-database tenancy.
π pgvector docs
Fix worth flagging β
The closed-shape storage rewrite in Marten 9 missed reading back mt_created_at, so [CreatedAt]-annotated members and m.CreatedAt.MapTo(...)-mapped fields weren't being populated after a load. #4577 restores the v8 behavior. If you saw "the timestamp is suddenly always default" after upgrading to 9.x, this is the patch.
Marten 9.2.0 / 9.2.1 β Lightweight Sessions Become the Default β
Two coordinated releases finally close out a long-standing semantic shift:
// In Marten 9.2.1, the IDocumentSession resolved from DI is now a
// LightweightSession by default β no identity map, no dirty tracking.
//
// If you previously depended on the identity-mapped default, opt in:
opts.UseIdentityMapSessions(); // or call OpenSession() explicitlyThis was the change captured in #4574 β "ACTUALLY making LightweightSessions the default." It's a quiet but high-impact semantic change. The migration guide has full detail; if you upgraded from Marten 8 and your code depended on identity-map semantics from the default session, read the lightweight-sessions docs before deploying.
Other 9.2.0 highlights:
IEventStore.AllDatabases()β store-agnostic accessor for every backingIEventDatabase, letting tooling (like CritterWatch) reach dead-letter counts, projection progress, and so on without coupling to a specific store type.- JasperFx 2.2.0 family bump across the board.
Marten 9.0.1 / 9.0.2 β The Source-Generator Patch Wave β
The first 72 hours after GA were dominated by source-generator polish. Several adopters hit edge cases the alpha cycle didn't surface:
#4557 β self-aggregating projections failed for consumers that referenced only the
Martenpackage. The generator now ships bundled as an analyzer in theMartenNuGet itself, so a plain<PackageReference Include="Marten" />runs it automatically.#4542 β
requiredmembers on self-aggregating snapshot types broke generated evolver construction. Fixed;default!is now only emitted when a public parameterless constructor exists, otherwiseRuntimeHelpers.GetUninitializedObjecttakes over.#4543 β nullable
[ReadAggregate]aggregate parameters generate correctly.Self-aggregating
recordaggregates no longer need aSnapshot<T>call site orpartialβ the generator now emits a self-aggregating evolver from therecorddeclaration itself, including cross-assembly cases.Opt-in STJ source-generation context for AOT/trim-friendly metadata:
SystemTextJsonSerializer.UseTypeInfoResolver(...)(#4540).
Big thanks to community contributors β especially @erdtsieck β for stress-testing the new source generator in real apps and filing the precise reports that made these fixes possible.
Wolverine 6.1.0 β AOT-Friendly Discovery (No More Runtime Type Scanning) β
Wolverine 6.1.0 closes out the AOT pillar in a big way: under TypeLoadMode.Static, Wolverine no longer does runtime GetTypes() / filesystem assembly scans for handlers, HTTP endpoints, gRPC services, or extensions. Discovery now flows through source-generated manifests baked at build time.
The full list:
| What's source-generated now | PRs |
|---|---|
| Handlers + message types | #2906 Β· #2928 |
| HTTP endpoints | #2925 Β· #2929 |
| gRPC services (incl. direct-mapped mode) | #2926 Β· #2930 Β· #2934 |
[WolverineHandlerModule] assemblies (no filesystem probe) | #2905 Β· #2935 |
| Extension discovery manifest | #2902 Β· #2918 |
| Generated HTTP/gRPC types attached by full name | #2908 Β· #2936 |
Everything else routed through JasperFx TypeQuery | #2909 Β· #2932 |
π AOT Publishing guide
The 6.1.0 release also includes a handful of new features that shipped through the 6.0.x patch line but didn't have a standalone GitHub release:
dotnet run -- wolverine-diagnostics describe-handlers <Type> β
CLI diagnostic that runs DescribeHandlerMatch from the command line β debug handler discovery without editing your bootstrapping code.
π Command Line Diagnostics tutorial Β· π Troubleshooting handler discovery
dotnet run -- openapi β
Build-time OpenAPI generation. No database or broker needed β generates the doc straight from the source-generated endpoint manifest.
dotnet run -- openapi --output ./swagger.jsonProduction-grade Roslyn-free deployment β
Docs + a worked sample showing how to drop WolverineFx.RuntimeCompilation from production images entirely once you're shipping pre-generated code.
Other quality-of-life landings in 6.1.0 β
MessageGroupIdon standard SQS queues for SQS fair queues (#2889)ClaimsPrincipalonSignalREnvelopefor message-level auth on SignalR transports (#2937)- EF Core outbox now flushes before the HTTP response is written (#2920)
- Mixed-lifetime
IEnumerable<T>support + Lamar fully removed; the built-inServiceProvideris the container (#2914)
Wolverine 6.2.0 β Result<T>, DbContext Abstractions, and a 90% Allocation Cut β
π Native Result<T> Support β Phase 1 β
First three phases of Result<T>-style handler return support landed: a ResultPolicy registry, handler-side unwrapping seams, and caller-side InvokeAsync<T> unwrap. This lays the groundwork for plugging in ErrorOr, OneOf, FluentResults, and similar libraries as first-class return-value shapes (#2952, refs the long-running #2221).
π EF Core DbContext Abstractions β
The EF Core transaction middleware now binds correctly when handler parameters are declared as interface or abstract base types over a concrete DbContext:
public interface IOrdersDbContext
{
DbSet<Order> Orders { get; }
Task<int> SaveChangesAsync(CancellationToken ct);
}
public static class PlaceOrderHandler
{
public static async Task Handle(PlaceOrder cmd, IOrdersDbContext db)
{
db.Orders.Add(new Order(cmd.OrderId, cmd.Total));
// Wolverine auto-applies the EF Core transaction & flushes the outbox.
}
}Multiple abstractions over the same concrete DbContext are supported in a single handler β the runtime resolves them all to the same scoped instance, and the transaction still auto-applies.
π DbContext Abstractions docs
β‘ Outgoing Envelope Pooling β
MessageRouter.RouteForPublish now pulls from the runtime envelope pool when the route's sender is an InlineSendingAgent or BufferedSendingAgent. Measured in the CritterStackScalability harness:
- β504 B/op (β90%) on transport-bound publish/send paths
- ~10Γ fewer Gen0 collections per 1k ops
DurableSendingAgent, local-queue agents, and ISenderRequiresCallback senders are excluded for now (different lifecycle plumbing required, tracked as follow-ups).
Bug fixes worth noting β
- Scheduled-cascade loss from
[ReadAggregate]/[DocumentExists]handlers (#2943) - Long Postgres queue names now routed through Weasel's
PostgresqlIdentifier.Shorten()so they don't overflow the 63-byte identifier limit (#2945) - MySQL
PersistNodeRecordSQL syntax error β unquoted schema identifier emit (#2946) - Pulsar
KeyNotFoundExceptionACKing batch messages on partitioned topics (#2950) - Remote-node agent
InvokeAsync<T>reply timeout bumped 10s β 30s (#2951)
Docs β
- AOT callout for FluentValidation in the HTTP validation guide
- Extended docs + scenario tests for the new DbContext abstractions
Polecat 4.1.0 / 4.2.0 β Dead-Letter Storage, LINQ DistinctBy, and Resilience Hardening β
Polecat (SQL Server-backed sibling of Marten) had a similarly productive week.
Polecat 4.1.0 highlights β
Projection dead-letter storage: failures under SkipApplyErrors (the JasperFx.Events 2.x default) are now persisted as DeadLetterEvent documents in pc_doc_deadletterevent, mirroring Marten's document-backed dead letters. The IEventDatabase count reads are implemented:
await database.CountDeadLetterEventsAsync(shardName);
var counts = await database.FetchDeadLetterCountsAsync();Resilience-pipeline hardening: all database command execution now flows through StoreOptions.ResiliencePipeline (Polly). Previously-unprotected paths are now covered β event-store progression reads, IEventStore explorer reads, the synchronous HiLo sequence path, and the LINQ non-stale-data poll.
Polecat 4.2.0 highlights β
LINQ DistinctBy() translated to SQL Server:
var distinct = await session.Query<Order>()
.DistinctBy(o => o.CustomerId)
.ToListAsync();Emitted as a ROW_NUMBER() windowed subquery partitioned by the key (SQL Server has no DISTINCT ON).
IEventStore.AllDatabases() override β same store-agnostic surface Marten 9.2.0 exposed, implemented on Polecat's DocumentStore.
Cross-Stack Theme: Coordinated Version Matrix β
A meta-theme of the week is how tightly the version matrix is now coordinated. A typical patch wave looks like:
JasperFx 2.2.1 ββ¬ββΊ Marten 9.3.x
βββΊ Polecat 4.2.x
βββΊ Wolverine 6.2.xA single change in JasperFx.Events.SourceGenerator (say) gets repoint PRs through every dependent repo within the same window β usually within hours. If you're on the GA majors, you'll see frequent patch upgrades, but NuGet's [*, *) solver and the matched alpha pinning in Directory.Packages.props mean the matrix stays consistent.
Community Spotlight β
This week's release notes are full of fixes traceable to specific community reports:
- @erdtsieck β multiple source-generator edge cases, the
messageContextduplicate-var codegen fix, theIReadOnlyListaggregate grouper signature - @LiteracyFanatic β SQS standard-queue
MessageGroupIdsupport - @patrick-cloke-simplisafe β Pulsar partitioned-topic ACK fix
- @XL1TTE β EF Core DbContext abstractions PR + MediatR shim handler discovery
- @kentcooper β duplicate
messageContextbug report - @isccliao β MySQL ANSI quoting bug
- @dbrcina β default-session-now-lightweight semantic regression
- @trung-nguyenduc β LINQ
DistinctBy()translation gap (fixed for SQL Server in Polecat) - @gergelyurbancsik β
LoadAsync<T>underConjoinedDefaulttenancy - @MeikelLP β source-generator delivery bug
- @dmytro-pryvedeniuk β broader resource disposal cleanup
If you're an adopter who hits something rough on the new GAs, file it. Most of the items above were reported and fixed in the same week.
Upgrading β
For most adopters, the safe upgrade path right now is:
Marten 9.3.3 + Polecat 4.2.1 + Wolverine 6.2.2 + JasperFx.* 2.2.1If you're upgrading from Marten 8, the two semantic shifts to know about are:
- Default
IDocumentSessionis now lightweight. See the migration guide. - Marten 9 uses source-generated projection dispatch β your self-aggregating types should compile out of the box now (post-9.0.2), but
partialis still required on*Projectionsubclasses.
If you're upgrading from Wolverine 5, the Wolverine 5 β 6 migration guide covers ServiceLocationPolicy, the Lamar removal, and the AOT story.
What's Next β
Looking ahead, the open work this week paints a clear picture:
Result<T>Phases 2+ β more idiomatic unwrapping, integration with the popularResultlibraries- CritterWatch clustering + RBAC + MCP β a new product surface taking shape on top of the stable libraries
- Cold-start performance pillar continuation β the source-generator wave is opening up further startup-snapshot work
The Critter Stack is in a healthy place: stable majors, sub-day patch cadence on real bugs, and a noticeably busy community PR queue. Keep the issues, PRs, and discussions coming.
Find us on the JasperFx Discord or open issues across the JasperFx GitHub org.

