
Critter Stack Roundup: Two Weeks, Six Repositories, and an EF Core Sweep
It’s been a busy couple of weeks across the Critter Stack. Between September 13th and today, we merged roughly 370 pull requests across JasperFx, Weasel, Marten, Polecat, Fisher, and Wolverine, and shipped a pile of releases along the way. More importantly, a big chunk of that work came from, or was driven by, people outside of JasperFx Software, so there’s a lot of thanking to do in this post.
Here’s the short version of what shipped:
| Project | Releases in the last two weeks |
|---|---|
| Wolverine | 6.37.0, 6.38.0, 6.39.0, 6.39.1, 6.40.0 |
| Marten | 9.34.0, 9.35.0, 9.36.0, 9.37.0, 9.38.0, 9.39.0, 9.39.1, 9.40.0 |
| Polecat | 5.27.0, 5.28.0, 5.28.1, 5.29.0, 5.30.0, 5.31.0, and now 5.32.0 |
| Weasel | 9.33.0, 9.34.0, 9.35.0, 9.35.1, 9.35.2 |
| JasperFx | 2.69.1 through 2.75.2 |
| Fisher | 1.5.0 through 1.13.0 |
And Polecat 5.32 will be released by the time you read this.
And the big themes:
- An Entity Framework Core sweep through Wolverine and Weasel, touched off by a set of sharp bug reports — I wouldn’t expect any of the bugs we address to impact many people, but I’d like the number of EF Core features not supported or features we don’t support well to be essentially zero
- Vector, full text, and hybrid search across Marten, Polecat, and Fisher, all behind one shared API – this is to support our forthcoming agentic memory tool “Stoat”
- Conjoined multi-tenancy tightened up and held to a shared compliance suite on every store. This was mostly a testing effort, but there were some EF Core related issues that helped spawn that work. It did find some issues with Polecat and Fisher when we did more compliance testing against Marten behavior to the younger tools
- Error messages that tell you what to do instead of just telling you something went wrong – I wrote about that previously in our new AI Skills 1.14 release notes
- Idempotency and agent distribution improvements in Wolverine, a lot of it from the community – Some things are just flat out hard. Gulp.
The EF Core Sweep
This one started with a trio of excellent issue reports from Ali Yuksekkaya against Wolverine’s EF Core integration:
- Conjoined EF Core tenancy was ignored in the
Lightweighttransaction mode, so a message could be written under the wrong tenant and HTTP cascades skipped the outbox (GH-4611) - Conjoined tenancy allowed detached updates and deletes to touch rows that belonged to another tenant (GH-4612)
- Returning
Update<T>orStore<T>from a handler saved nothing at all for an entity theDbContextwasn’t already tracking (GH-4613)
Those got fixed, but reports like that are a signal that nobody had been looking hard enough at that corner of the code, so we went looking. The resulting sweep landed as a wave of Wolverine pull requests this weekend:
- Lightweight EF Core message handlers now get a real transactional outbox. Before, Lightweight mode quietly meant no outbox enlistment, no domain event scraping, and no idempotency check
- Domain event envelopes scraped out of the
DbContextare now flushed before the Eager transaction commits. This was a leftover from an earlier fix where the scraped envelopes were tracked after the onlySaveChanges()call and never persisted - Wolverine’s conjoined tenant query filter now composes with your query filter instead of replacing it. EF Core 9 and EF Core 10 behave differently here (EF 9 discards the earlier filter, EF 10 throws), and we now cover both
- A new
EfCoreOpsfamily of declarative side effects forExecuteUpdate,ExecuteDelete, raw SQL, and bulk inserts. These force the Eager transaction mode they need and scope themselves to the current tenant - A conjoined tenancy test battery that now runs against Marten, Polecat, and Fisher backed message stores
- Closing several test coverage holes, including owned, complex, and JSON mapped models end to end
::: warning One of these changes is technically breaking. If you’re using AutoApplyTransactions() and a single handler could be claimed by two persistence providers (say, it takes both a DbContext and a Marten IDocumentSession), Wolverine used to silently apply no transaction at all. It now fails loudly at startup and tells you how to resolve it (GH-4631). If your application hits this, it had a real bug that this change is surfacing. :::
The sweep reached down into Weasel as well, which is where our EF Core schema migration support lives. Ali also reported that EF Core batched queries silently returned incomplete entities for owned, complex, and JSON members, and then contributed a follow up pull request to prepare each batched query once while keeping the provider’s parameter types intact. On top of that, Weasel now:
- Materializes EF Core batched queries through EF Core itself
- Carries database indexes that EF Core can’t model as their own DDL
- Maps the columns of table-split complex properties, which were previously omitted and then dropped by
CreateOrUpdate - Never drops a column from an EF Core derived table just because the model doesn’t happen to declare it
Marten, Polecat, and Fisher also all fixed the same bug where an EF Core backed projection leaked the DbContext it created per batch. Thanks to wpei-infotrack for reporting the Marten version of that one, which turned out to be a leaked PostgreSQL connection per batch in the async daemon.
Vector, Full Text, and Hybrid Search Everywhere
The other big feature push was around search. JasperFx now has a shared, store-neutral vector and hybrid search surface in JasperFx.Events.Vectors, and all three of our document stores implement it:
- Marten.PgVector moved onto the shared contracts with scored search, HNSW index declarations, and hybrid search using reciprocal rank fusion over PostgreSQL’s
ts_rankand the vector leg. Marten also now warns you when a full text search falls back to an unindexed, whole document scan - Polecat picked up vector search on SQL Server 2025’s native
VECTORtype, a Polecat-owned full text inverted index with LINQ operators and BM25 scoring, prefix search, and hybrid search on top of both - Fisher got hybrid search and embeddings produced from an event stream
Because they all implement the same contract, there’s now a DocumentSearchCompliance suite in JasperFx that holds all three stores to the same behavior. As usual, the first real run of that suite found defects in the suite itself as well as in the stores, which is exactly what it’s for.
Polecat 5.32
Polecat 5.32 is the release that rolls up the last few days of work, and it’s largely about parity with Marten and about multi-tenancy:
- Conjoined document tenancy sweep. Every document shape is now tested in both directions, and conjoined tenancy now also holds on the event, vector, and partition onboarding paths
- Raw SQL in
IBatchedQuery, bringing batching up to parity with Marten - Strongly typed identifiers are now assigned onto a live aggregated aggregate, just like Marten does
- Document indexes, computed columns, and foreign keys are now modeled as Weasel schema objects, which means
db-dumpfinally reproduces the full configured schema - Every tenant database is described in the store’s usage descriptor for a multi-tenanted store, which matters for CritterWatch
- Event store diagnostic reads answer “no results” rather than throwing when the schema was never applied or has drifted
- Adoption of JasperFx 2.75 and Weasel 9.35
Earlier in the window, Polecat 5.31 also made startup migrations take a real cross-process lock through sp_getapplock and routed the last few hand-escaped SQL construction sites through a shared escaping helper.
Multi-Tenancy, Everywhere
Multi-tenancy was a recurring thread through all six repositories:
- JasperFx now has conjoined document tenancy compliance tests, and Marten, Polecat, and Fisher all enrolled
TenantIdStyleis now applied consistently. Marten applies it at every boundary that stamped or keyed on the raw tenant id, and Wolverine now normalizesEnvelope.TenantIdthrough it so that the stores reading that value write the right tenant- There’s a new canonical
DisabledTenantExceptionin JasperFx. All the stores and Wolverine now refuse a disabled tenant with that exception instead of reporting “Unknown tenant id” IEventStore.OpenReadOnlyEventStore(tenantId)is now tenant aware, so the read-only tier is actually reachable in multi-tenanted systems- Marten and Fisher both fixed bugs with the diagnostic and explorer reads that CritterWatch depends on, including one in Marten where a read against an unknown tenant under sharded tenancy could provision a new tenant and run DDL
Error Messages That Name the Remedy
I spent a chunk of the last two weeks going through the exception messages across the stack, asking one question of each: does this tell the user what to do next? A lot of them didn’t. That turned into a wave of small pull requests:
- Wolverine saga failures, handler discovery, missing aggregates, oversized Azure Service Bus messages, mismatched RabbitMQ queue declarations, missing Redis streams, missing HTTP transport clients, and SNS configuration problems all name the remedy now. Named connection strings are validated in one pass at startup. The SignalR transport fails the host start if the hub refuses the connection. Wolverine.HTTP gets a one-line opt in for mapping concurrency failures to a 409
ProblemDetails, and an unknown tenant id maps to a 404 instead of a 500 - Marten stream identity mismatches, stream collisions, LINQ refusals, and the rich append concurrency exception all got clearer
- Weasel decodes
sp_getapplockfailures, translates database permission failures into a typed exception, and now warns beforeAutoCreate.Alldrops and recreates an object - JasperFx lifted canonical
ArchivedStreamException,DisabledTenantException, and stream exceptions so that all three stores throw the same types with the same guidance
On a related note, Marten 9.39 includes two SQL injection fixes, for GroupBy() HAVING comparison operands and for full text search regConfig values on every sink, not just the WHERE clause. If you’re on an older 9.x version, please upgrade.
Wolverine
Besides the EF Core work above, here are some of the highlights in Wolverine:
- Capacity aware agent assignment. Michael Harris contributed per-node capacity ceilings for agent distribution (GH-3959), so one node dying no longer pushes its entire share onto the survivors. Anne Erdtsieck filed the original issue, and also contributed a fix for group affinity placement during blue/green deployments. There’s new documentation for the whole thing
- Transactional deduplication. Wolverine’s deduplication claim can now ride the native Marten, Polecat, or Fisher transaction. Laurence Gillian reported that an HTTP deduplication claim survived a non-2xx response and turned legitimate retries into false duplicates, and that’s fixed too
- An Oracle external table transport contributed by Travis Kirke, along with a fix for the Oracle durability agent’s incoming message recovery
- Kafka replay fixes from Marko Lahma
- GCP Pub/Sub now shares one subscription across nodes by default, thanks to a report from bittercoder about duplicated messages
- Topology scoped message grouping rules, from a request by Anne Erdtsieck
- Recurring schedule operability with occurrence attribution and a manual trigger, and a fix for non-UTC recurring schedules. Both came from issues filed by Babu Annamalai
- OpenTelemetry parenting fixes. Recurring messages, inline receivers, and Wolverine’s internal agent loops no longer inherit whatever
Activityhappened to be current when they were started. Marten had a similar fix for spans being re-parented to their grandparent, reported by bohdan-hukivskyi. Open Telemetry sometimes has some weird behavior in terms of how parents are tracked. I expect or hope this will help the CritterWatch graphing of Otel spans from Wolverine - Tore Hammervoll fixed
TypeLoadMode.Staticso a handler chain finds its pre-generated type by full name instead of scanning exported types per chain - Two concurrency fixes reported by Marcin Aumiler: delayed sends to a partitioned PostgreSQL queue could be deleted without ever being handled, and the listener collection could be corrupted when agents started in parallel
Marten
Other than the search work, multi-tenancy, and messages, Marten had a lot of community driven fixes:
- Anne Erdtsieck fixed the outer projection of
GroupJoin/SelectManyandGROUP BYrendering over a join, made the projection batch fault properly when an operation can’t be configured, and made an unprovisioned event store answer “nothing” for its progression and dead letter tables - Erik Shafer fixed patched documents and replaced events to be stamped with the session’s actual instant (reported by BaerMitUmlaut)
- vpetrevski routed
QuickWithServerTimestampsstream starts throughmt_quick_append_eventsto avoid sequence gaps - Mark van der Dam fixed quoting of duplicated field column names
- Raymond Masciarella made partitioned stream archiving idempotent
- Arnel Robles corrected the pgvector docs and reported two async daemon bugs in the skip-ahead loader and progression writes
- tychomensing-topicus reported a
Select()projection problem with absent JSON keys
Weasel
Besides the EF Core work, Weasel got two nice community contributions. Joel Reinford made SQL Server migration scripts runnable under sqlcmd and safe to re-run, and Jaedyn moved us onto the patched advisory lock. Anne Erdtsieck also contributed a change to let the schema delta decide when an index needs a concurrent build.
JasperFx
JasperFx is just a foundational shared library, but a lot happened there:
- The shared vector and hybrid search surface described above
- A store-agnostic
StubEventStream<T>for unit testing event sourced handlers, with documentation on all three stores - The
@jasperfx/event-model-vuerenderer moved into the JasperFx repository, next to the Event Model descriptor it draws, and the Event Model now handles services that host more than one model - Hardening the aggregate source generator, including an opt-in build-time assertion that the generator is actually attached
- Andre Vieira fixed
codegen testfailing for every message handler since Wolverine 6.37, and Alan Klimowski fixed a code generation frame ordering issue (and a duplicated service declaration in Wolverine.HTTP)
Fisher
Fisher went from 1.5 to 1.13 in two weeks. Beyond the search work, Fisher now creates its event store tables on first use, supports directory tenancy on Windows, validates tenant ids before turning them into file names, fixes decimal comparisons in LINQ, and requires the source generator with a smoke test of the packed package. Kebin contributed a fix for enlisted sessions with an inline projection registered.
Thank You
The Critter Stack only gets this good because people use it hard (thanks?), tell us when it breaks with actionable error reports, and increasingly send in the fix too. Thank you to everybody who contributed code over the past two weeks:
Ali Yuksekkaya, Anne Erdtsieck, Michael Harris, Travis Kirke, Marko Lahma, Laurence Gillian, Tore Hammervoll, Alan Klimowski, Erik Shafer, Andre Vieira, Jakob Tikjøb Andersen, Joel Reinford, Jaedyn, Mark van der Dam, Raymond Masciarella, Arnel Robles, vpetrevski, Kebin, Marcin Aumiler, Jorge L. Torres M, and Rayan-and-beyond.
And to everyone who filed a good issue with a reproduction, including ArieGato, michielpeeters, raypet-visma, AndreiKopylov, framos-varajo, syserr500, BaharAtNode, Petteroe, zxjon22, r0ss88, bittercoder, BaerMitUmlaut, wpei-infotrack, bohdan-hukivskyi, tychomensing-topicus, and Babu Annamalai: those reports are what drove a lot of this.
No, seriously, the Critter Stack community is as far as we can tell far, far about average for OSS projects in terms of how helpful the community is to help drive and improve the tools.