Honor [Consumes] / IAcceptsMetadata on form endpoints#2589
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes OpenAPI request-format discovery for Wolverine form endpoints so that [Consumes] / IAcceptsMetadata is reflected in ApiDescription.SupportedRequestFormats, preventing ASP.NET Core OpenAPI from silently defaulting form endpoints to application/x-www-form-urlencoded and breaking generated multipart clients.
Changes:
- Seed
SupportedRequestFormatsfromIAcceptsMetadatafor form endpoints when no formats are already present. - Add a regression endpoint that declares
[Consumes("multipart/form-data")]on a form-bound parameter. - Add a regression test asserting multipart is present in
SupportedRequestFormatsfor the new endpoint.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Http/WolverineWebApi/Forms/FormEndpoints.cs | Adds a form endpoint decorated with [Consumes("multipart/form-data")] to reproduce the issue. |
| src/Http/Wolverine.Http/HttpChain.ApiDescription.cs | Extends CreateApiDescription to flow IAcceptsMetadata into SupportedRequestFormats for form endpoints. |
| src/Http/Wolverine.Http.Tests/using_form_parameters.cs | Adds a regression test ensuring form endpoints honor consumes metadata in SupportedRequestFormats. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| foreach (var metadata in Endpoint!.Metadata.OfType<IAcceptsMetadata>()) | ||
| { | ||
| foreach (var contentType in metadata.ContentTypes) | ||
| { | ||
| apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat | ||
| { | ||
| MediaType = contentType | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return apiDescription; |
| // fillRequestType only honors [Consumes] / IAcceptsMetadata for body | ||
| // endpoints (HasRequestType && !IsFormData). Without this, form | ||
| // endpoints fall through and ASP.NET Core OpenAPI's GetFormRequestBody | ||
| // defaults SupportedRequestFormats to "application/x-www-form-urlencoded" | ||
| // — silently dropping [Consumes("multipart/form-data")] on file-upload |
| // IAcceptsMetadata when the endpoint has a body request type and is | ||
| // not a form endpoint. Form endpoints used to fall through and rely | ||
| // on ASP.NET Core OpenAPI's "application/x-www-form-urlencoded" | ||
| // default, which silently dropped [Consumes("multipart/form-data")] | ||
| // and caused generated clients (Orval, NSwag) to emit URLSearchParams | ||
| // bodies instead of multipart for file-upload endpoints. |
|
@rmasciarella Thank you for taking this on. OpenAPI is about my least favorite part of working on Wolverine. |
jeremydmiller
left a comment
There was a problem hiding this comment.
Approving — the form-endpoint OpenAPI fix is correct and the regression test demonstrates the bug clearly. Will follow up with the Copilot review's dedup recommendation in a separate PR.
|
@jeremydmiller no worries it just came up for me while vibe coding, i did run it through a few different models from different angles to make sure before i put it up, glad that it was helpful. |
Updated [WolverineFx](http://github.com/jasperfx/wolverine) from 5.32.1 to 5.36.2. <details> <summary>Release notes</summary> _Sourced from [WolverineFx's releases](http://github.com/jasperfx/wolverine/releases)._ ## 5.36.1 **Upgrade to this version please if you use the EF Core transactional middleware and are using a very recent version of Wolverine** ## What's Changed * fix(efcore): suppress duplicate FlushOutgoingMessages in Eager-mode HTTP chains by @jeremydmiller in JasperFx/wolverine#2663 **Full Changelog**: JasperFx/wolverine@V5.36.0...v5.36.1 ## 5.36.0 ## What's Changed * Add IAgent.Description for monitoring-tool tooltips by @jeremydmiller in JasperFx/wolverine#2645 * feat: AES-256-GCM message encryption with send/receive enforcement (#2643) by @BlackChepo in JasperFx/wolverine#2644 * fix(rdbms-inbox): discard duplicate Kafka messages instead of freezing the partition (#2639) by @BlackChepo in JasperFx/wolverine#2648 * Prevent leadership re-election every 5 minutes by @Bishbulb in JasperFx/wolverine#2625 * Enrich DurabilityAgent.CheckHealthAsync with persistence-layer signals (#2646) by @jeremydmiller in JasperFx/wolverine#2649 * Native API versioning for Wolverine.Http (closes #2627) by @outofrange-consulting in JasperFx/wolverine#2633 * Enrich StickyPostgresqlQueueListenerAgent.CheckHealthAsync with listener-state signals (#2647) by @jeremydmiller in JasperFx/wolverine#2650 * fix(rdbms-inbox): align RavenDb and CosmosDb batch inbox with #2648 duplicate-envelope contract by @BlackChepo in JasperFx/wolverine#2651 * Surface 'Custom' interop mode + DefaultSerializerDescription on EndpointDescriptor (#2641) by @jeremydmiller in JasperFx/wolverine#2652 * 5.36: routing-precedence regression fix + metrics test repair + 5.36.0 cut by @jeremydmiller in JasperFx/wolverine#2655 * feat(durability): AlwaysMakeScheduledMessagesDurable policy by @jeremydmiller in JasperFx/wolverine#2657 **Full Changelog**: JasperFx/wolverine@V5.35.2...V5.36.0 ## 5.35.2 ## What's Changed A small bug-fix and dependency-refresh release. ### Persistence fixes * **Oracle: timestamp default expressions now survive non-UTC DB sessions** (#2634) — `OracleMessageStore.Initialize` and the surrounding schema declared timestamp columns (`health_check`, `started`, `posted`, etc.) with a DEFAULT of `SYS_EXTRACT_UTC(SYSTIMESTAMP)`. That returns a TIMESTAMP without a time zone, and when implicitly cast into the `TIMESTAMP WITH TIME ZONE` column Oracle stamps it with the *session* time zone — so for any session in UTC+N the just-persisted value's UTC equivalent was N hours in the past. That tripped the `NodeAgentController.DoHealthChecksAsync` staleness filter and surfaced as a `NullReferenceException` during the first leadership-election heartbeat (`self!.AssignAgents([LeaderUri])` on a `null` self). All 18 occurrences swapped to `SYSTIMESTAMP AT TIME ZONE 'UTC'`, with quote-doubling for the DDL contexts that go through Weasel's `EXECUTE IMMEDIATE '...'` wrapper. ### Reliability hardening * **Lock `dbcontrol://` and `oraclecontrol://` endpoints to `BufferedInMemory`** (#2637) — the database-backed control transport carries inter-node leader-election and agent-reassignment commands. Marking either endpoint Durable would route every control envelope through the same store-backed inbox/outbox the durability agent itself owns (deadlock); marking it Inline contradicts the batched-poll semantics. Both `DatabaseControlEndpoint` and `OracleControlEndpoint` now override `supportsMode` so any policy that tries to flip them off `BufferedInMemory` either silently skips (`UseDurableInboxOnAllListeners`, `UseDurableOutboxOnAllSendingEndpoints`, etc., already check `SupportsMode`) or fails fast with a clear `InvalidOperationException`. ### Source-gen / startup * **Aggregate `IWolverineTypeLoader` across all known assemblies (#2632)** by @devployment — since 5.34.0, hosts on the source-generated codegen path silently dropped handlers that lived in *referenced* assemblies because `tryDiscoverTypeLoaderFromAttribute` only inspected `Options.ApplicationAssembly`. First invocation of one of those handlers threw `IndeterminateRoutesException`. The runtime now walks `ApplicationAssembly` *and* every assembly in `Discovery.Assemblies`, collects every `[WolverineTypeManifest]` loader it finds, and exposes their union via the new internal `CompositeWolverineTypeLoader`. Existing single-loader semantics are preserved when only one manifest is found. ### Other * **Saga timeout flag + scheduled-dispatch Activity tag (#2631)** by @jeremydmiller — surfaces saga timeout and scheduled-dispatch metadata so the upcoming CritterWatch saga visualization can render in-flight saga state. ### Dependencies * **Bump JasperFx 1.28.0 → 1.28.2 and JasperFx.Events 1.29.0 → 1.31.1** (#2638). **Full Changelog**: JasperFx/wolverine@V5.35.1...V5.35.2 ## 5.35.1 ## What's Changed A bug-fix and small-feature release covering Oracle-in-Balanced-mode startup, a duplicate-poller defect on RavenDb, an AWS SQS sharded-slot naming fix, and a new SagaDescriptor surface for CritterWatch. ### Persistence fixes * **Oracle in `DurabilityMode.Balanced` now boots** (#2622) — `OracleMessageStore.Initialize` was a no-op, so hosts using Oracle persistence in Balanced mode failed to start with `ArgumentOutOfRangeException("ControlEndpoint cannot be null for this usage")`. The fix mirrors `MessageDatabase.Initialize` for Oracle and stands up a parallel `OracleControlTransport` / `OracleControlEndpoint` / `OracleControlSender` / `OracleControlListener` set under the new `oraclecontrol://` protocol — necessary because the shared `DatabaseControlSender` / `DatabaseControlListener` assume `@`-prefixed placeholders and Guid values that map directly onto a `DbParameter`, neither of which Oracle accepts (`:`-prefixed placeholders, `RAW(16)` id columns requiring `byte[]`). Also fixes a latent `OracleMessageStore.EnqueueAsync` no-op that left `LogRecordsAsync` silently dropping node records, and makes `FetchRecentRecordsAsync` `DBNull`-safe on the description column. Adds a new `OracleTests.LeaderElection` project so the leadership-compliance suite runs against Oracle (marked `[Trait("Category","Flaky")]` because the suite needs careful TM/DML lock sequencing between back-to-back runs). * **RavenDb: only one durability agent polls after host start** (#2623, #2629) by @Bishbulb (root-cause investigation and original fix) and @jeremydmiller — `RavenDbMessageStore.StartScheduledJobs` eagerly built and started a `RavenDbDurabilityAgent` at boot in addition to the agent that `NodeAgentController` already builds and starts via `IAgentFamily` / `MessageStoreCollection`. Two `RavenDbDurabilityAgent` instances then polled the same database concurrently, both believed they held the scheduled-job lock, raced to mark the same envelopes `Incoming`, and surfaced `ConcurrencyException` plus double-fired timeouts. Drops the eager `StartTimers()` call; the cluster-managed agent is now the single owner of polling. The agent returned from `StartScheduledJobs` is held by `WolverineRuntime.DurableScheduledJobs` purely for its disposal-time `StopAsync`. Comes with a reflection-free regression test (added `RavenDbDurabilityAgent.IsPolling` and `CompositeAgent.InnerAgents` for the test to enumerate without poking at private fields). A companion regression-guard test for CosmosDb is included; investigation showed the equivalent CosmosDb path does NOT have the bug today (`CosmosDbMessageStore.BuildAgentFamily` returns null and `Uri` uses the `cosmosdb://` scheme rather than `wolverinedb://`, so `MessageStoreCollection` never registers a competing agent). ### Other improvements * **AWS SQS: correct naming for sharded slot endpoints** in #1f294ce5 — fixes a regression where sharded slot endpoint URIs were assembled with the wrong segment ordering, causing the consumer side to listen at the wrong queue. * **`SagaDescriptor` exported via `ServiceCapabilities`** in #f0998c7b — adds a saga-shape descriptor (saga type, id type, current state) to the capabilities exporter so CritterWatch can surface saga inventory and current state in its dashboard. **Full Changelog**: JasperFx/wolverine@V5.34.0...V5.35.1 ## 5.34.0 ## What's Changed This release introduces three new features (Claim Check pattern, declarative Polecat data requirements, and a new opt-in `WolverineFx.RuntimeCompilation` package as the first step toward AOT compatibility), a fix for a long-standing service-location footgun, several real RavenDB and EF Core durability fixes, plus the cold-start prep work done as part of issue #1577. ### Highlights * **Add Claim Check / DataBus pattern with `[Blob]` attribute (#2412)** by @jeremydmiller in #2617 — off-load large message-property payloads to external storage on send and re-hydrate them on receive, so the on-the-wire envelope stays small. Core abstractions (`IClaimCheckStore`, `ClaimCheckToken`, `[Blob]`, `FileSystemClaimCheckStore`, `opts.UseClaimCheck(...)`) live in `Wolverine.Persistence`. Two new backend NuGet packages ship alongside: `WolverineFx.ClaimCheck.AzureBlobStorage` and `WolverineFx.ClaimCheck.AmazonS3`. New documentation page under `/guide/durability/claim-checks`. * **Declarative data-requirement attributes for Polecat (#2552)** by @jeremydmiller in #2615 — `[DocumentExists<T>]`, `[DocumentDoesNotExist<T>]`, and `PolecatOps.Document<T>().MustExist(...)` / `.MustNotExist(...)` mirror the Marten ergonomics, with the same batch-query optimization that folds multiple existence checks into a single Polecat `IBatchedQuery` round-trip. * **Service-located `IMessageBus` / `IMessageContext` now see the active context (#2583)** by @jeremydmiller in #2616 — when user code service-locates `IMessageBus` (e.g., constructor injection on a service the handler resolves at runtime), it now receives the same `MessageContext` the handler itself uses, preserving outbox semantics. Implemented as a per-chain opt-in (gated on `Chain.UsesServiceLocation`) so chains that don't service-locate pay zero `AsyncLocal` overhead per message. * **Cold-start optimizations and `WolverineFx.RuntimeCompilation` opt-in package (#1577)** by @jeremydmiller in #2613 and #cc00ca0a — first pass at the cold-start / AOT roadmap. Wires the `Wolverine.SourceGeneration` analyzer into the `WolverineFx` NuGet output (so source-generated `IWolverineTypeLoader` discovery flows transitively to consumers), pre-populates the `WolverineMessageNaming` cache during startup to eliminate first-message reflection cost, applies `[DynamicallyAccessedMembers]` annotations on the `Activator.CreateInstance` call sites for trimmer-friendliness, and adds `ConfigureAwait(false)` across 117 awaits on the per-message hot path. The new `WolverineFx.RuntimeCompilation` package becomes the future-facing opt-in API for runtime Roslyn compilation; default behavior is preserved. ### RavenDB durability fixes * **Fix bulk RavenDB inbox store leaking duplicates as inbox-unavailable (#2606)** by @Bishbulb in #2606 * **Take over expired RavenDB scheduled-job and leadership locks (#2608)** by @Bishbulb in #2608 * **Disable optimistic concurrency on RavenDB cluster-wide node sessions; load-then-modify on agent-assignment writes (#2610)** by @Bishbulb in #2610 ### EF Core / ancillary stores * **Fix `DurableLocalQueue` to route incoming envelopes to ancillary stores (#2611)** by @jeremydmiller — handler chains targeting an ancillary message store now have their incoming envelopes persisted in that store rather than the main store, fixing a class of "envelope stuck as Incoming forever" bugs. * **Fix `WolverineEnabled` annotation collision in ancillary-store EF Core scenarios** as part of #2618 — `MapWolverineEnvelopeStorage` is now idempotent when called against the same model graph more than once. ### Other improvements * **Fix `MessageBus` clobbering per-message `Envelope.Source`** in #26e38002 — `MessageBus.Send` was overwriting the per-message `Source` with the host's `ServiceName`, masking the originating service in causation tracking. * **Surface upcoming `ServiceLocationPolicy.NotAllowed` v6 default (#2584)** by @jeremydmiller in #2609 — chains that resolve dependencies via service location now log a warning at codegen time, with a clear migration path to the new opt-in `Wolverine.RuntimeCompilation` story for v6. ### CI stabilization * **Several flaky test classes fixed or marked** in #2612 and #2618 — timeout bumps, `IDisposable` → `IAsyncLifetime` conversions, lifecycle cleanup; chronically broken-on-CI classes (`SqliteTests.Transport.multi_tenancy_with_multiple_files`, `CosmosDbTests.LeaderElection.leader_election`, `Wolverine.RabbitMQ.Tests.send_by_topics`, `Wolverine.RabbitMQ.Tests.sending_raw_messages`) tagged `[Trait("Category", "Flaky")]` with header comments documenting the symptom and the real follow-up fix. * **JasperFx 1.28 / RuntimeCompiler 4.5 ambiguity fallout fixed** in #2618 — files importing both `JasperFx.CodeGeneration` and `JasperFx.RuntimeCompiler` now compile cleanly after the `InitializeSynchronously` extension method was moved into `JasperFx.CodeGeneration` upstream. ### Issue-tracker housekeeping * `#2507` (Quartz.Net / TickerQ first-class integration) added to the 6.0 milestone with a phased plan documented on the issue. * `#1577` (cold-start optimization roadmap) updated with a six-phase plan and current-state checklist; phases 1–4 are non-breaking on v5.x and largely landed in this release. **Full Changelog**: JasperFx/wolverine@V5.33.0...V5.34.0 ## 5.33.0 ## What's Changed This release includes a major reliability fix for distributed leader election, a port of the Polecat 2.x event store integration, and several other quality-of-life improvements. ### Highlights * **Fix #2602: Leader split-brain via stale advisory lock** by @jeremydmiller in #2607 — three-layer defensive fix that detects when a Postgres/SQL Server/MySQL/Oracle/SQLite advisory lock has been silently released server-side, steps down the local leader, releases its agents, and triggers a fresh leader election. Adds a new `LeadershipLost` node record type and an `IWolverineObserver.LostLeadership()` hook for monitoring. * **Polecat 2.x event store integration** by @jeremydmiller in #2598 — ports the aggregate handler workflow from Marten so SQL Server-backed projects can use Polecat as a native event store with Wolverine. Requires SQL Server 2025 for the native `JSON` type. * **Fix #2571: Preserve context fields on scheduled-send wrap/unwrap** by @jeremydmiller in #2605 — saga IDs, tenant IDs, and other correlation fields now survive the scheduled-send envelope round-trip. * **gRPC enhancements** by @jeremydmiller in #2565 — middleware weaving, validate convention, user exception mapping, bidirectional streaming, code-first codegen, plus new samples. * **Allow RabbitMQ exchanges to be declared passive** by @jeremydmiller in #2574 ### Other Improvements * Move non-sticky-handlers guard inside the compile lock by @jeremydmiller in #2556 * Add `launchSettings.json` to sample projects by @jeremydmiller in #2600 **Full Changelog**: JasperFx/wolverine@V5.32.1...V5.33.0 ## What's Changed * Update vulnerable OpenTelemetry dependencies by @dmytro-pryvedeniuk in JasperFx/wolverine#2590 * Fix tenant partitioning precedence with inferred grouping by @erdtsieck in JasperFx/wolverine#2581 * Use quoted schema names in SQL statements for RDBMS persistence by @esskar in JasperFx/wolverine#2577 * Skip logging when the count is zero by @Bishbulb in JasperFx/wolverine#2560 * Accept *Async suffix on saga method names (#2578) by @jeremydmiller in JasperFx/wolverine#2593 * Fix ancillary store scheduled messages stuck Incoming forever (#2576) by @jeremydmiller in JasperFx/wolverine#2591 * Add Wolverine.SourceGeneration to slnx (supersedes #2575) by @jeremydmiller in JasperFx/wolverine#2592 * Honor [Consumes] / IAcceptsMetadata on form endpoints by @rmasciarella in JasperFx/wolverine#2589 * Materialize EF domain-event scraper before publishing (#2585) by @jeremydmiller in JasperFx/wolverine#2594 * Process Manager via Handlers sample by @erikshafer in JasperFx/wolverine#2579 * Fix missing ParameterDescriptor for form parameters in API descriptions by @rmasciarella in JasperFx/wolverine#2587 * Allow configuration of the MessageBatchTimeout by @lyall-sc in JasperFx/wolverine#2582 * Fix #2588: durable outbox policy ignored for conventionally-routed senders by @jeremydmiller in JasperFx/wolverine#2596 * Fix #2595: explicit DeliveryOptions.SagaId on saga Start cascades should win by @jeremydmiller in JasperFx/wolverine#2597 * Allow RabbitMQ exchanges to be declared passive by @saithis in JasperFx/wolverine#2574 * Fix NoHandlerForEndpointException during concurrent saga chain compile by @Bishbulb in JasperFx/wolverine#2556 * gRPC: Middleware Weaving, Validate Convention, User Exception Mapping, Bidi Streaming, Code-First Codegen, New Samples by @erikshafer in JasperFx/wolverine#2565 * Add launchSettings.json files to the sample projects by @dmytro-pryvedeniuk in JasperFx/wolverine#2600 * Fix #2571: preserve context fields on scheduled-send wrap unwrap by @jeremydmiller in JasperFx/wolverine#2605 * Add Endpoint.BrokerRole for CritterWatch endpoint display (#2601) by @jeremydmiller in JasperFx/wolverine#2603 * Process Manager via Handlers Cleanup by @erikshafer in JasperFx/wolverine#2604 * Fix #2602: leader split-brain via stale advisory-lock state by @jeremydmiller in JasperFx/wolverine#2607 * Polecat parity port from Wolverine.Marten + Polecat 2.1.0 bump by @jeremydmiller in JasperFx/wolverine#2598 ## New Contributors * @esskar made their first contribution in JasperFx/wolverine#2577 * @rmasciarella made their first contribution in JasperFx/wolverine#2589 * @saithis made their first contribution in JasperFx/wolverine#2574 **Full Changelog**: JasperFx/wolverine@V5.32.1...V5.33.0 Commits viewable in [compare view](http://github.com/jasperfx/wolverine/commits). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updated [WolverineFx.Kafka](http://github.com/jasperfx/wolverine) from 5.32.1 to 5.39.0. <details> <summary>Release notes</summary> _Sourced from [WolverineFx.Kafka's releases](http://github.com/jasperfx/wolverine/releases)._ ## 5.39.0 ## What's Changed * docs(logging): correct OpenTelemetry AddMeter sample by @stigrune in JasperFx/wolverine#2707 * Add nuke/build.schema.json to .gitignore by @dmytro-pryvedeniuk in JasperFx/wolverine#2706 * Allow NATS messages without message-type header when DefaultIncomingMessage is set by @frankvdb7 in JasperFx/wolverine#2703 * fix(http): honor [RoutePrefix] when no global prefix is configured by @outofrange-consulting in JasperFx/wolverine#2708 * Fault Events — Auto-Publish `Fault<T>` on Terminal Handler Failure by @BlackChepo in JasperFx/wolverine#2695 * Outbox behavior alignment with EF Core transaction model by @Ferchke7 in JasperFx/wolverine#2677 * Preserve correlation/trace context across Global Partitioning interceptor (supersedes #2709) by @jeremydmiller in JasperFx/wolverine#2710 * feat(nats): expose JetStream DeliverPolicy on transport + listener by @jeremydmiller in JasperFx/wolverine#2711 * fix(build): include Wolverine.HealthChecks in the Pack target by @jeremydmiller in JasperFx/wolverine#2712 * feat(sqs): transport-wide DefaultDeadLetterQueueName with per-listener override precedence by @jeremydmiller in JasperFx/wolverine#2714 ## New Contributors * @stigrune made their first contribution in JasperFx/wolverine#2707 **Full Changelog**: JasperFx/wolverine@V5.38.0...V5.39.0 ## 5.38.0 ## What's Changed * Disable build-time document generation by @dmytro-pryvedeniuk in JasperFx/wolverine#2684 * Add IBrokerHealthProbe + RabbitMQ implementation (CritterWatch#70) by @jeremydmiller in JasperFx/wolverine#2686 * Add Wolverine.HealthChecks for ASP.NET Core IHealthCheck integration (CritterWatch#73) by @jeremydmiller in JasperFx/wolverine#2687 * Add WolverineHeartbeat emission (CritterWatch#72) by @jeremydmiller in JasperFx/wolverine#2688 * fix(agents): never let self fall into the staleNodes filter (GH-2682) by @jeremydmiller in JasperFx/wolverine#2689 * feat(http): api-versioning headers on all responses + multi-version handler support by @outofrange-consulting in JasperFx/wolverine#2683 * fix(advisory-lock): make TryAttainLockAsync idempotent against re-entrant calls by @jeremydmiller in JasperFx/wolverine#2691 * fix(test): lock AllItemsReceived state in batch_processing tests by @jeremydmiller in JasperFx/wolverine#2692 * fix(marten): publish IEvent.TenantId verbatim under conjoined tenancy by @jeremydmiller in JasperFx/wolverine#2693 * fix(rabbitmq): bind handler queue to every handled-message exchange under FromHandlerType by @jeremydmiller in JasperFx/wolverine#2697 * fix(sqlite): close #2680 by bumping Weasel to 8.15.1 by @jeremydmiller in JasperFx/wolverine#2696 * [1/N] dynamic listeners — foundation only (GH-2685) by @jeremydmiller in JasperFx/wolverine#2699 * feat(tracing): opt-in handler-execution diagnostics on WolverineOptions.Tracking by @jeremydmiller in JasperFx/wolverine#2698 * Fix unbounded retry loop on durable receiver shutdown (GH-2671) by @jeremydmiller in JasperFx/wolverine#2701 * [2/N] real RDBMS-backed IListenerStore (GH-2685) by @jeremydmiller in JasperFx/wolverine#2700 * [3/N] DynamicListenerAgentFamily — cluster-coordinated dynamic listeners (GH-2685) by @jeremydmiller in JasperFx/wolverine#2702 **Full Changelog**: JasperFx/wolverine@V5.37.2...V5.38.0 ## 5.37.2 ## What's Changed * Fixing a regression problem with explicit type discovery getting lost… by @jeremydmiller in JasperFx/wolverine#2678 * chore(core): remove Wolverine-specific Roslyn source generator by @jeremydmiller in JasperFx/wolverine#2679 **Full Changelog**: JasperFx/wolverine@V5.37.0...V5.37.2 ## 5.37.0 ## What's Changed * The real fix to our EF Core + Outbox problem. Bump to 5.36.2 by @jeremydmiller in JasperFx/wolverine#2665 * fix(sqlite): use BEGIN EXCLUSIVE for migration lock; TTL sweep + heartbeat for non-migration advisory locks by @mysticmind in JasperFx/wolverine#2666 * feat(http): support [ApiVersionNeutral] for opt-out endpoints by @outofrange-consulting in JasperFx/wolverine#2660 * feat(critterwatch): polecat outbox listener fix + DocumentStores capability surface by @jeremydmiller in JasperFx/wolverine#2672 * feat(rabbitmq): public API for multi-node cluster endpoints (#2659) by @BlackChepo in JasperFx/wolverine#2664 * fix(marten): GH-2669 alternative — Uri-based ancillary store routing (no IMessageStore.Id dependency) by @jeremydmiller in JasperFx/wolverine#2674 **Full Changelog**: JasperFx/wolverine@v5.36.1...V5.37.0 ## 5.36.1 **Upgrade to this version please if you use the EF Core transactional middleware and are using a very recent version of Wolverine** ## What's Changed * fix(efcore): suppress duplicate FlushOutgoingMessages in Eager-mode HTTP chains by @jeremydmiller in JasperFx/wolverine#2663 **Full Changelog**: JasperFx/wolverine@V5.36.0...v5.36.1 ## 5.36.0 ## What's Changed * Add IAgent.Description for monitoring-tool tooltips by @jeremydmiller in JasperFx/wolverine#2645 * feat: AES-256-GCM message encryption with send/receive enforcement (#2643) by @BlackChepo in JasperFx/wolverine#2644 * fix(rdbms-inbox): discard duplicate Kafka messages instead of freezing the partition (#2639) by @BlackChepo in JasperFx/wolverine#2648 * Prevent leadership re-election every 5 minutes by @Bishbulb in JasperFx/wolverine#2625 * Enrich DurabilityAgent.CheckHealthAsync with persistence-layer signals (#2646) by @jeremydmiller in JasperFx/wolverine#2649 * Native API versioning for Wolverine.Http (closes #2627) by @outofrange-consulting in JasperFx/wolverine#2633 * Enrich StickyPostgresqlQueueListenerAgent.CheckHealthAsync with listener-state signals (#2647) by @jeremydmiller in JasperFx/wolverine#2650 * fix(rdbms-inbox): align RavenDb and CosmosDb batch inbox with #2648 duplicate-envelope contract by @BlackChepo in JasperFx/wolverine#2651 * Surface 'Custom' interop mode + DefaultSerializerDescription on EndpointDescriptor (#2641) by @jeremydmiller in JasperFx/wolverine#2652 * 5.36: routing-precedence regression fix + metrics test repair + 5.36.0 cut by @jeremydmiller in JasperFx/wolverine#2655 * feat(durability): AlwaysMakeScheduledMessagesDurable policy by @jeremydmiller in JasperFx/wolverine#2657 **Full Changelog**: JasperFx/wolverine@V5.35.2...V5.36.0 ## 5.35.2 ## What's Changed A small bug-fix and dependency-refresh release. ### Persistence fixes * **Oracle: timestamp default expressions now survive non-UTC DB sessions** (#2634) — `OracleMessageStore.Initialize` and the surrounding schema declared timestamp columns (`health_check`, `started`, `posted`, etc.) with a DEFAULT of `SYS_EXTRACT_UTC(SYSTIMESTAMP)`. That returns a TIMESTAMP without a time zone, and when implicitly cast into the `TIMESTAMP WITH TIME ZONE` column Oracle stamps it with the *session* time zone — so for any session in UTC+N the just-persisted value's UTC equivalent was N hours in the past. That tripped the `NodeAgentController.DoHealthChecksAsync` staleness filter and surfaced as a `NullReferenceException` during the first leadership-election heartbeat (`self!.AssignAgents([LeaderUri])` on a `null` self). All 18 occurrences swapped to `SYSTIMESTAMP AT TIME ZONE 'UTC'`, with quote-doubling for the DDL contexts that go through Weasel's `EXECUTE IMMEDIATE '...'` wrapper. ### Reliability hardening * **Lock `dbcontrol://` and `oraclecontrol://` endpoints to `BufferedInMemory`** (#2637) — the database-backed control transport carries inter-node leader-election and agent-reassignment commands. Marking either endpoint Durable would route every control envelope through the same store-backed inbox/outbox the durability agent itself owns (deadlock); marking it Inline contradicts the batched-poll semantics. Both `DatabaseControlEndpoint` and `OracleControlEndpoint` now override `supportsMode` so any policy that tries to flip them off `BufferedInMemory` either silently skips (`UseDurableInboxOnAllListeners`, `UseDurableOutboxOnAllSendingEndpoints`, etc., already check `SupportsMode`) or fails fast with a clear `InvalidOperationException`. ### Source-gen / startup * **Aggregate `IWolverineTypeLoader` across all known assemblies (#2632)** by @devployment — since 5.34.0, hosts on the source-generated codegen path silently dropped handlers that lived in *referenced* assemblies because `tryDiscoverTypeLoaderFromAttribute` only inspected `Options.ApplicationAssembly`. First invocation of one of those handlers threw `IndeterminateRoutesException`. The runtime now walks `ApplicationAssembly` *and* every assembly in `Discovery.Assemblies`, collects every `[WolverineTypeManifest]` loader it finds, and exposes their union via the new internal `CompositeWolverineTypeLoader`. Existing single-loader semantics are preserved when only one manifest is found. ### Other * **Saga timeout flag + scheduled-dispatch Activity tag (#2631)** by @jeremydmiller — surfaces saga timeout and scheduled-dispatch metadata so the upcoming CritterWatch saga visualization can render in-flight saga state. ### Dependencies * **Bump JasperFx 1.28.0 → 1.28.2 and JasperFx.Events 1.29.0 → 1.31.1** (#2638). **Full Changelog**: JasperFx/wolverine@V5.35.1...V5.35.2 ## 5.35.1 ## What's Changed A bug-fix and small-feature release covering Oracle-in-Balanced-mode startup, a duplicate-poller defect on RavenDb, an AWS SQS sharded-slot naming fix, and a new SagaDescriptor surface for CritterWatch. ### Persistence fixes * **Oracle in `DurabilityMode.Balanced` now boots** (#2622) — `OracleMessageStore.Initialize` was a no-op, so hosts using Oracle persistence in Balanced mode failed to start with `ArgumentOutOfRangeException("ControlEndpoint cannot be null for this usage")`. The fix mirrors `MessageDatabase.Initialize` for Oracle and stands up a parallel `OracleControlTransport` / `OracleControlEndpoint` / `OracleControlSender` / `OracleControlListener` set under the new `oraclecontrol://` protocol — necessary because the shared `DatabaseControlSender` / `DatabaseControlListener` assume `@`-prefixed placeholders and Guid values that map directly onto a `DbParameter`, neither of which Oracle accepts (`:`-prefixed placeholders, `RAW(16)` id columns requiring `byte[]`). Also fixes a latent `OracleMessageStore.EnqueueAsync` no-op that left `LogRecordsAsync` silently dropping node records, and makes `FetchRecentRecordsAsync` `DBNull`-safe on the description column. Adds a new `OracleTests.LeaderElection` project so the leadership-compliance suite runs against Oracle (marked `[Trait("Category","Flaky")]` because the suite needs careful TM/DML lock sequencing between back-to-back runs). * **RavenDb: only one durability agent polls after host start** (#2623, #2629) by @Bishbulb (root-cause investigation and original fix) and @jeremydmiller — `RavenDbMessageStore.StartScheduledJobs` eagerly built and started a `RavenDbDurabilityAgent` at boot in addition to the agent that `NodeAgentController` already builds and starts via `IAgentFamily` / `MessageStoreCollection`. Two `RavenDbDurabilityAgent` instances then polled the same database concurrently, both believed they held the scheduled-job lock, raced to mark the same envelopes `Incoming`, and surfaced `ConcurrencyException` plus double-fired timeouts. Drops the eager `StartTimers()` call; the cluster-managed agent is now the single owner of polling. The agent returned from `StartScheduledJobs` is held by `WolverineRuntime.DurableScheduledJobs` purely for its disposal-time `StopAsync`. Comes with a reflection-free regression test (added `RavenDbDurabilityAgent.IsPolling` and `CompositeAgent.InnerAgents` for the test to enumerate without poking at private fields). A companion regression-guard test for CosmosDb is included; investigation showed the equivalent CosmosDb path does NOT have the bug today (`CosmosDbMessageStore.BuildAgentFamily` returns null and `Uri` uses the `cosmosdb://` scheme rather than `wolverinedb://`, so `MessageStoreCollection` never registers a competing agent). ### Other improvements * **AWS SQS: correct naming for sharded slot endpoints** in #1f294ce5 — fixes a regression where sharded slot endpoint URIs were assembled with the wrong segment ordering, causing the consumer side to listen at the wrong queue. * **`SagaDescriptor` exported via `ServiceCapabilities`** in #f0998c7b — adds a saga-shape descriptor (saga type, id type, current state) to the capabilities exporter so CritterWatch can surface saga inventory and current state in its dashboard. **Full Changelog**: JasperFx/wolverine@V5.34.0...V5.35.1 ## 5.34.0 ## What's Changed This release introduces three new features (Claim Check pattern, declarative Polecat data requirements, and a new opt-in `WolverineFx.RuntimeCompilation` package as the first step toward AOT compatibility), a fix for a long-standing service-location footgun, several real RavenDB and EF Core durability fixes, plus the cold-start prep work done as part of issue #1577. ### Highlights * **Add Claim Check / DataBus pattern with `[Blob]` attribute (#2412)** by @jeremydmiller in #2617 — off-load large message-property payloads to external storage on send and re-hydrate them on receive, so the on-the-wire envelope stays small. Core abstractions (`IClaimCheckStore`, `ClaimCheckToken`, `[Blob]`, `FileSystemClaimCheckStore`, `opts.UseClaimCheck(...)`) live in `Wolverine.Persistence`. Two new backend NuGet packages ship alongside: `WolverineFx.ClaimCheck.AzureBlobStorage` and `WolverineFx.ClaimCheck.AmazonS3`. New documentation page under `/guide/durability/claim-checks`. * **Declarative data-requirement attributes for Polecat (#2552)** by @jeremydmiller in #2615 — `[DocumentExists<T>]`, `[DocumentDoesNotExist<T>]`, and `PolecatOps.Document<T>().MustExist(...)` / `.MustNotExist(...)` mirror the Marten ergonomics, with the same batch-query optimization that folds multiple existence checks into a single Polecat `IBatchedQuery` round-trip. * **Service-located `IMessageBus` / `IMessageContext` now see the active context (#2583)** by @jeremydmiller in #2616 — when user code service-locates `IMessageBus` (e.g., constructor injection on a service the handler resolves at runtime), it now receives the same `MessageContext` the handler itself uses, preserving outbox semantics. Implemented as a per-chain opt-in (gated on `Chain.UsesServiceLocation`) so chains that don't service-locate pay zero `AsyncLocal` overhead per message. * **Cold-start optimizations and `WolverineFx.RuntimeCompilation` opt-in package (#1577)** by @jeremydmiller in #2613 and #cc00ca0a — first pass at the cold-start / AOT roadmap. Wires the `Wolverine.SourceGeneration` analyzer into the `WolverineFx` NuGet output (so source-generated `IWolverineTypeLoader` discovery flows transitively to consumers), pre-populates the `WolverineMessageNaming` cache during startup to eliminate first-message reflection cost, applies `[DynamicallyAccessedMembers]` annotations on the `Activator.CreateInstance` call sites for trimmer-friendliness, and adds `ConfigureAwait(false)` across 117 awaits on the per-message hot path. The new `WolverineFx.RuntimeCompilation` package becomes the future-facing opt-in API for runtime Roslyn compilation; default behavior is preserved. ### RavenDB durability fixes * **Fix bulk RavenDB inbox store leaking duplicates as inbox-unavailable (#2606)** by @Bishbulb in #2606 * **Take over expired RavenDB scheduled-job and leadership locks (#2608)** by @Bishbulb in #2608 * **Disable optimistic concurrency on RavenDB cluster-wide node sessions; load-then-modify on agent-assignment writes (#2610)** by @Bishbulb in #2610 ### EF Core / ancillary stores * **Fix `DurableLocalQueue` to route incoming envelopes to ancillary stores (#2611)** by @jeremydmiller — handler chains targeting an ancillary message store now have their incoming envelopes persisted in that store rather than the main store, fixing a class of "envelope stuck as Incoming forever" bugs. * **Fix `WolverineEnabled` annotation collision in ancillary-store EF Core scenarios** as part of #2618 — `MapWolverineEnvelopeStorage` is now idempotent when called against the same model graph more than once. ### Other improvements * **Fix `MessageBus` clobbering per-message `Envelope.Source`** in #26e38002 — `MessageBus.Send` was overwriting the per-message `Source` with the host's `ServiceName`, masking the originating service in causation tracking. * **Surface upcoming `ServiceLocationPolicy.NotAllowed` v6 default (#2584)** by @jeremydmiller in #2609 — chains that resolve dependencies via service location now log a warning at codegen time, with a clear migration path to the new opt-in `Wolverine.RuntimeCompilation` story for v6. ### CI stabilization * **Several flaky test classes fixed or marked** in #2612 and #2618 — timeout bumps, `IDisposable` → `IAsyncLifetime` conversions, lifecycle cleanup; chronically broken-on-CI classes (`SqliteTests.Transport.multi_tenancy_with_multiple_files`, `CosmosDbTests.LeaderElection.leader_election`, `Wolverine.RabbitMQ.Tests.send_by_topics`, `Wolverine.RabbitMQ.Tests.sending_raw_messages`) tagged `[Trait("Category", "Flaky")]` with header comments documenting the symptom and the real follow-up fix. * **JasperFx 1.28 / RuntimeCompiler 4.5 ambiguity fallout fixed** in #2618 — files importing both `JasperFx.CodeGeneration` and `JasperFx.RuntimeCompiler` now compile cleanly after the `InitializeSynchronously` extension method was moved into `JasperFx.CodeGeneration` upstream. ### Issue-tracker housekeeping * `#2507` (Quartz.Net / TickerQ first-class integration) added to the 6.0 milestone with a phased plan documented on the issue. * `#1577` (cold-start optimization roadmap) updated with a six-phase plan and current-state checklist; phases 1–4 are non-breaking on v5.x and largely landed in this release. **Full Changelog**: JasperFx/wolverine@V5.33.0...V5.34.0 ## 5.33.0 ## What's Changed This release includes a major reliability fix for distributed leader election, a port of the Polecat 2.x event store integration, and several other quality-of-life improvements. ### Highlights * **Fix #2602: Leader split-brain via stale advisory lock** by @jeremydmiller in #2607 — three-layer defensive fix that detects when a Postgres/SQL Server/MySQL/Oracle/SQLite advisory lock has been silently released server-side, steps down the local leader, releases its agents, and triggers a fresh leader election. Adds a new `LeadershipLost` node record type and an `IWolverineObserver.LostLeadership()` hook for monitoring. * **Polecat 2.x event store integration** by @jeremydmiller in #2598 — ports the aggregate handler workflow from Marten so SQL Server-backed projects can use Polecat as a native event store with Wolverine. Requires SQL Server 2025 for the native `JSON` type. * **Fix #2571: Preserve context fields on scheduled-send wrap/unwrap** by @jeremydmiller in #2605 — saga IDs, tenant IDs, and other correlation fields now survive the scheduled-send envelope round-trip. * **gRPC enhancements** by @jeremydmiller in #2565 — middleware weaving, validate convention, user exception mapping, bidirectional streaming, code-first codegen, plus new samples. * **Allow RabbitMQ exchanges to be declared passive** by @jeremydmiller in #2574 ### Other Improvements * Move non-sticky-handlers guard inside the compile lock by @jeremydmiller in #2556 * Add `launchSettings.json` to sample projects by @jeremydmiller in #2600 **Full Changelog**: JasperFx/wolverine@V5.32.1...V5.33.0 ## What's Changed * Update vulnerable OpenTelemetry dependencies by @dmytro-pryvedeniuk in JasperFx/wolverine#2590 * Fix tenant partitioning precedence with inferred grouping by @erdtsieck in JasperFx/wolverine#2581 * Use quoted schema names in SQL statements for RDBMS persistence by @esskar in JasperFx/wolverine#2577 * Skip logging when the count is zero by @Bishbulb in JasperFx/wolverine#2560 * Accept *Async suffix on saga method names (#2578) by @jeremydmiller in JasperFx/wolverine#2593 * Fix ancillary store scheduled messages stuck Incoming forever (#2576) by @jeremydmiller in JasperFx/wolverine#2591 * Add Wolverine.SourceGeneration to slnx (supersedes #2575) by @jeremydmiller in JasperFx/wolverine#2592 * Honor [Consumes] / IAcceptsMetadata on form endpoints by @rmasciarella in JasperFx/wolverine#2589 * Materialize EF domain-event scraper before publishing (#2585) by @jeremydmiller in JasperFx/wolverine#2594 * Process Manager via Handlers sample by @erikshafer in JasperFx/wolverine#2579 * Fix missing ParameterDescriptor for form parameters in API descriptions by @rmasciarella in JasperFx/wolverine#2587 * Allow configuration of the MessageBatchTimeout by @lyall-sc in JasperFx/wolverine#2582 * Fix #2588: durable outbox policy ignored for conventionally-routed senders by @jeremydmiller in JasperFx/wolverine#2596 * Fix #2595: explicit DeliveryOptions.SagaId on saga Start cascades should win by @jeremydmiller in JasperFx/wolverine#2597 * Allow RabbitMQ exchanges to be declared passive by @saithis in JasperFx/wolverine#2574 * Fix NoHandlerForEndpointException during concurrent saga chain compile by @Bishbulb in JasperFx/wolverine#2556 * gRPC: Middleware Weaving, Validate Convention, User Exception Mapping, Bidi Streaming, Code-First Codegen, New Samples by @erikshafer in JasperFx/wolverine#2565 * Add launchSettings.json files to the sample projects by @dmytro-pryvedeniuk in JasperFx/wolverine#2600 * Fix #2571: preserve context fields on scheduled-send wrap unwrap by @jeremydmiller in JasperFx/wolverine#2605 * Add Endpoint.BrokerRole for CritterWatch endpoint display (#2601) by @jeremydmiller in JasperFx/wolverine#2603 * Process Manager via Handlers Cleanup by @erikshafer in JasperFx/wolverine#2604 * Fix #2602: leader split-brain via stale advisory-lock state by @jeremydmiller in JasperFx/wolverine#2607 * Polecat parity port from Wolverine.Marten + Polecat 2.1.0 bump by @jeremydmiller in JasperFx/wolverine#2598 ## New Contributors * @esskar made their first contribution in JasperFx/wolverine#2577 * @rmasciarella made their first contribution in JasperFx/wolverine#2589 * @saithis made their first contribution in JasperFx/wolverine#2574 **Full Changelog**: JasperFx/wolverine@V5.32.1...V5.33.0 Commits viewable in [compare view](http://github.com/jasperfx/wolverine/compare/V5.32.1...V5.39.0). </details> Pinned [WolverineFx.Postgresql](http://github.com/jasperfx/wolverine) at 5.39.0. <details> <summary>Release notes</summary> _Sourced from [WolverineFx.Postgresql's releases](http://github.com/jasperfx/wolverine/releases)._ ## 5.39.0 ## What's Changed * docs(logging): correct OpenTelemetry AddMeter sample by @stigrune in JasperFx/wolverine#2707 * Add nuke/build.schema.json to .gitignore by @dmytro-pryvedeniuk in JasperFx/wolverine#2706 * Allow NATS messages without message-type header when DefaultIncomingMessage is set by @frankvdb7 in JasperFx/wolverine#2703 * fix(http): honor [RoutePrefix] when no global prefix is configured by @outofrange-consulting in JasperFx/wolverine#2708 * Fault Events — Auto-Publish `Fault<T>` on Terminal Handler Failure by @BlackChepo in JasperFx/wolverine#2695 * Outbox behavior alignment with EF Core transaction model by @Ferchke7 in JasperFx/wolverine#2677 * Preserve correlation/trace context across Global Partitioning interceptor (supersedes #2709) by @jeremydmiller in JasperFx/wolverine#2710 * feat(nats): expose JetStream DeliverPolicy on transport + listener by @jeremydmiller in JasperFx/wolverine#2711 * fix(build): include Wolverine.HealthChecks in the Pack target by @jeremydmiller in JasperFx/wolverine#2712 * feat(sqs): transport-wide DefaultDeadLetterQueueName with per-listener override precedence by @jeremydmiller in JasperFx/wolverine#2714 ## New Contributors * @stigrune made their first contribution in JasperFx/wolverine#2707 **Full Changelog**: JasperFx/wolverine@V5.38.0...V5.39.0 ## 5.38.0 ## What's Changed * Disable build-time document generation by @dmytro-pryvedeniuk in JasperFx/wolverine#2684 * Add IBrokerHealthProbe + RabbitMQ implementation (CritterWatch#70) by @jeremydmiller in JasperFx/wolverine#2686 * Add Wolverine.HealthChecks for ASP.NET Core IHealthCheck integration (CritterWatch#73) by @jeremydmiller in JasperFx/wolverine#2687 * Add WolverineHeartbeat emission (CritterWatch#72) by @jeremydmiller in JasperFx/wolverine#2688 * fix(agents): never let self fall into the staleNodes filter (GH-2682) by @jeremydmiller in JasperFx/wolverine#2689 * feat(http): api-versioning headers on all responses + multi-version handler support by @outofrange-consulting in JasperFx/wolverine#2683 * fix(advisory-lock): make TryAttainLockAsync idempotent against re-entrant calls by @jeremydmiller in JasperFx/wolverine#2691 * fix(test): lock AllItemsReceived state in batch_processing tests by @jeremydmiller in JasperFx/wolverine#2692 * fix(marten): publish IEvent.TenantId verbatim under conjoined tenancy by @jeremydmiller in JasperFx/wolverine#2693 * fix(rabbitmq): bind handler queue to every handled-message exchange under FromHandlerType by @jeremydmiller in JasperFx/wolverine#2697 * fix(sqlite): close #2680 by bumping Weasel to 8.15.1 by @jeremydmiller in JasperFx/wolverine#2696 * [1/N] dynamic listeners — foundation only (GH-2685) by @jeremydmiller in JasperFx/wolverine#2699 * feat(tracing): opt-in handler-execution diagnostics on WolverineOptions.Tracking by @jeremydmiller in JasperFx/wolverine#2698 * Fix unbounded retry loop on durable receiver shutdown (GH-2671) by @jeremydmiller in JasperFx/wolverine#2701 * [2/N] real RDBMS-backed IListenerStore (GH-2685) by @jeremydmiller in JasperFx/wolverine#2700 * [3/N] DynamicListenerAgentFamily — cluster-coordinated dynamic listeners (GH-2685) by @jeremydmiller in JasperFx/wolverine#2702 **Full Changelog**: JasperFx/wolverine@V5.37.2...V5.38.0 ## 5.37.2 ## What's Changed * Fixing a regression problem with explicit type discovery getting lost… by @jeremydmiller in JasperFx/wolverine#2678 * chore(core): remove Wolverine-specific Roslyn source generator by @jeremydmiller in JasperFx/wolverine#2679 **Full Changelog**: JasperFx/wolverine@V5.37.0...V5.37.2 ## 5.37.0 ## What's Changed * The real fix to our EF Core + Outbox problem. Bump to 5.36.2 by @jeremydmiller in JasperFx/wolverine#2665 * fix(sqlite): use BEGIN EXCLUSIVE for migration lock; TTL sweep + heartbeat for non-migration advisory locks by @mysticmind in JasperFx/wolverine#2666 * feat(http): support [ApiVersionNeutral] for opt-out endpoints by @outofrange-consulting in JasperFx/wolverine#2660 * feat(critterwatch): polecat outbox listener fix + DocumentStores capability surface by @jeremydmiller in JasperFx/wolverine#2672 * feat(rabbitmq): public API for multi-node cluster endpoints (#2659) by @BlackChepo in JasperFx/wolverine#2664 * fix(marten): GH-2669 alternative — Uri-based ancillary store routing (no IMessageStore.Id dependency) by @jeremydmiller in JasperFx/wolverine#2674 **Full Changelog**: JasperFx/wolverine@v5.36.1...V5.37.0 ## 5.36.1 **Upgrade to this version please if you use the EF Core transactional middleware and are using a very recent version of Wolverine** ## What's Changed * fix(efcore): suppress duplicate FlushOutgoingMessages in Eager-mode HTTP chains by @jeremydmiller in JasperFx/wolverine#2663 **Full Changelog**: JasperFx/wolverine@V5.36.0...v5.36.1 ## 5.36.0 ## What's Changed * Add IAgent.Description for monitoring-tool tooltips by @jeremydmiller in JasperFx/wolverine#2645 * feat: AES-256-GCM message encryption with send/receive enforcement (#2643) by @BlackChepo in JasperFx/wolverine#2644 * fix(rdbms-inbox): discard duplicate Kafka messages instead of freezing the partition (#2639) by @BlackChepo in JasperFx/wolverine#2648 * Prevent leadership re-election every 5 minutes by @Bishbulb in JasperFx/wolverine#2625 * Enrich DurabilityAgent.CheckHealthAsync with persistence-layer signals (#2646) by @jeremydmiller in JasperFx/wolverine#2649 * Native API versioning for Wolverine.Http (closes #2627) by @outofrange-consulting in JasperFx/wolverine#2633 * Enrich StickyPostgresqlQueueListenerAgent.CheckHealthAsync with listener-state signals (#2647) by @jeremydmiller in JasperFx/wolverine#2650 * fix(rdbms-inbox): align RavenDb and CosmosDb batch inbox with #2648 duplicate-envelope contract by @BlackChepo in JasperFx/wolverine#2651 * Surface 'Custom' interop mode + DefaultSerializerDescription on EndpointDescriptor (#2641) by @jeremydmiller in JasperFx/wolverine#2652 * 5.36: routing-precedence regression fix + metrics test repair + 5.36.0 cut by @jeremydmiller in JasperFx/wolverine#2655 * feat(durability): AlwaysMakeScheduledMessagesDurable policy by @jeremydmiller in JasperFx/wolverine#2657 **Full Changelog**: JasperFx/wolverine@V5.35.2...V5.36.0 ## 5.35.2 ## What's Changed A small bug-fix and dependency-refresh release. ### Persistence fixes * **Oracle: timestamp default expressions now survive non-UTC DB sessions** (#2634) — `OracleMessageStore.Initialize` and the surrounding schema declared timestamp columns (`health_check`, `started`, `posted`, etc.) with a DEFAULT of `SYS_EXTRACT_UTC(SYSTIMESTAMP)`. That returns a TIMESTAMP without a time zone, and when implicitly cast into the `TIMESTAMP WITH TIME ZONE` column Oracle stamps it with the *session* time zone — so for any session in UTC+N the just-persisted value's UTC equivalent was N hours in the past. That tripped the `NodeAgentController.DoHealthChecksAsync` staleness filter and surfaced as a `NullReferenceException` during the first leadership-election heartbeat (`self!.AssignAgents([LeaderUri])` on a `null` self). All 18 occurrences swapped to `SYSTIMESTAMP AT TIME ZONE 'UTC'`, with quote-doubling for the DDL contexts that go through Weasel's `EXECUTE IMMEDIATE '...'` wrapper. ### Reliability hardening * **Lock `dbcontrol://` and `oraclecontrol://` endpoints to `BufferedInMemory`** (#2637) — the database-backed control transport carries inter-node leader-election and agent-reassignment commands. Marking either endpoint Durable would route every control envelope through the same store-backed inbox/outbox the durability agent itself owns (deadlock); marking it Inline contradicts the batched-poll semantics. Both `DatabaseControlEndpoint` and `OracleControlEndpoint` now override `supportsMode` so any policy that tries to flip them off `BufferedInMemory` either silently skips (`UseDurableInboxOnAllListeners`, `UseDurableOutboxOnAllSendingEndpoints`, etc., already check `SupportsMode`) or fails fast with a clear `InvalidOperationException`. ### Source-gen / startup * **Aggregate `IWolverineTypeLoader` across all known assemblies (#2632)** by @devployment — since 5.34.0, hosts on the source-generated codegen path silently dropped handlers that lived in *referenced* assemblies because `tryDiscoverTypeLoaderFromAttribute` only inspected `Options.ApplicationAssembly`. First invocation of one of those handlers threw `IndeterminateRoutesException`. The runtime now walks `ApplicationAssembly` *and* every assembly in `Discovery.Assemblies`, collects every `[WolverineTypeManifest]` loader it finds, and exposes their union via the new internal `CompositeWolverineTypeLoader`. Existing single-loader semantics are preserved when only one manifest is found. ### Other * **Saga timeout flag + scheduled-dispatch Activity tag (#2631)** by @jeremydmiller — surfaces saga timeout and scheduled-dispatch metadata so the upcoming CritterWatch saga visualization can render in-flight saga state. ### Dependencies * **Bump JasperFx 1.28.0 → 1.28.2 and JasperFx.Events 1.29.0 → 1.31.1** (#2638). **Full Changelog**: JasperFx/wolverine@V5.35.1...V5.35.2 ## 5.35.1 ## What's Changed A bug-fix and small-feature release covering Oracle-in-Balanced-mode startup, a duplicate-poller defect on RavenDb, an AWS SQS sharded-slot naming fix, and a new SagaDescriptor surface for CritterWatch. ### Persistence fixes * **Oracle in `DurabilityMode.Balanced` now boots** (#2622) — `OracleMessageStore.Initialize` was a no-op, so hosts using Oracle persistence in Balanced mode failed to start with `ArgumentOutOfRangeException("ControlEndpoint cannot be null for this usage")`. The fix mirrors `MessageDatabase.Initialize` for Oracle and stands up a parallel `OracleControlTransport` / `OracleControlEndpoint` / `OracleControlSender` / `OracleControlListener` set under the new `oraclecontrol://` protocol — necessary because the shared `DatabaseControlSender` / `DatabaseControlListener` assume `@`-prefixed placeholders and Guid values that map directly onto a `DbParameter`, neither of which Oracle accepts (`:`-prefixed placeholders, `RAW(16)` id columns requiring `byte[]`). Also fixes a latent `OracleMessageStore.EnqueueAsync` no-op that left `LogRecordsAsync` silently dropping node records, and makes `FetchRecentRecordsAsync` `DBNull`-safe on the description column. Adds a new `OracleTests.LeaderElection` project so the leadership-compliance suite runs against Oracle (marked `[Trait("Category","Flaky")]` because the suite needs careful TM/DML lock sequencing between back-to-back runs). * **RavenDb: only one durability agent polls after host start** (#2623, #2629) by @Bishbulb (root-cause investigation and original fix) and @jeremydmiller — `RavenDbMessageStore.StartScheduledJobs` eagerly built and started a `RavenDbDurabilityAgent` at boot in addition to the agent that `NodeAgentController` already builds and starts via `IAgentFamily` / `MessageStoreCollection`. Two `RavenDbDurabilityAgent` instances then polled the same database concurrently, both believed they held the scheduled-job lock, raced to mark the same envelopes `Incoming`, and surfaced `ConcurrencyException` plus double-fired timeouts. Drops the eager `StartTimers()` call; the cluster-managed agent is now the single owner of polling. The agent returned from `StartScheduledJobs` is held by `WolverineRuntime.DurableScheduledJobs` purely for its disposal-time `StopAsync`. Comes with a reflection-free regression test (added `RavenDbDurabilityAgent.IsPolling` and `CompositeAgent.InnerAgents` for the test to enumerate without poking at private fields). A companion regression-guard test for CosmosDb is included; investigation showed the equivalent CosmosDb path does NOT have the bug today (`CosmosDbMessageStore.BuildAgentFamily` returns null and `Uri` uses the `cosmosdb://` scheme rather than `wolverinedb://`, so `MessageStoreCollection` never registers a competing agent). ### Other improvements * **AWS SQS: correct naming for sharded slot endpoints** in #1f294ce5 — fixes a regression where sharded slot endpoint URIs were assembled with the wrong segment ordering, causing the consumer side to listen at the wrong queue. * **`SagaDescriptor` exported via `ServiceCapabilities`** in #f0998c7b — adds a saga-shape descriptor (saga type, id type, current state) to the capabilities exporter so CritterWatch can surface saga inventory and current state in its dashboard. **Full Changelog**: JasperFx/wolverine@V5.34.0...V5.35.1 ## 5.34.0 ## What's Changed This release introduces three new features (Claim Check pattern, declarative Polecat data requirements, and a new opt-in `WolverineFx.RuntimeCompilation` package as the first step toward AOT compatibility), a fix for a long-standing service-location footgun, several real RavenDB and EF Core durability fixes, plus the cold-start prep work done as part of issue #1577. ### Highlights * **Add Claim Check / DataBus pattern with `[Blob]` attribute (#2412)** by @jeremydmiller in #2617 — off-load large message-property payloads to external storage on send and re-hydrate them on receive, so the on-the-wire envelope stays small. Core abstractions (`IClaimCheckStore`, `ClaimCheckToken`, `[Blob]`, `FileSystemClaimCheckStore`, `opts.UseClaimCheck(...)`) live in `Wolverine.Persistence`. Two new backend NuGet packages ship alongside: `WolverineFx.ClaimCheck.AzureBlobStorage` and `WolverineFx.ClaimCheck.AmazonS3`. New documentation page under `/guide/durability/claim-checks`. * **Declarative data-requirement attributes for Polecat (#2552)** by @jeremydmiller in #2615 — `[DocumentExists<T>]`, `[DocumentDoesNotExist<T>]`, and `PolecatOps.Document<T>().MustExist(...)` / `.MustNotExist(...)` mirror the Marten ergonomics, with the same batch-query optimization that folds multiple existence checks into a single Polecat `IBatchedQuery` round-trip. * **Service-located `IMessageBus` / `IMessageContext` now see the active context (#2583)** by @jeremydmiller in #2616 — when user code service-locates `IMessageBus` (e.g., constructor injection on a service the handler resolves at runtime), it now receives the same `MessageContext` the handler itself uses, preserving outbox semantics. Implemented as a per-chain opt-in (gated on `Chain.UsesServiceLocation`) so chains that don't service-locate pay zero `AsyncLocal` overhead per message. * **Cold-start optimizations and `WolverineFx.RuntimeCompilation` opt-in package (#1577)** by @jeremydmiller in #2613 and #cc00ca0a — first pass at the cold-start / AOT roadmap. Wires the `Wolverine.SourceGeneration` analyzer into the `WolverineFx` NuGet output (so source-generated `IWolverineTypeLoader` discovery flows transitively to consumers), pre-populates the `WolverineMessageNaming` cache during startup to eliminate first-message reflection cost, applies `[DynamicallyAccessedMembers]` annotations on the `Activator.CreateInstance` call sites for trimmer-friendliness, and adds `ConfigureAwait(false)` across 117 awaits on the per-message hot path. The new `WolverineFx.RuntimeCompilation` package becomes the future-facing opt-in API for runtime Roslyn compilation; default behavior is preserved. ### RavenDB durability fixes * **Fix bulk RavenDB inbox store leaking duplicates as inbox-unavailable (#2606)** by @Bishbulb in #2606 * **Take over expired RavenDB scheduled-job and leadership locks (#2608)** by @Bishbulb in #2608 * **Disable optimistic concurrency on RavenDB cluster-wide node sessions; load-then-modify on agent-assignment writes (#2610)** by @Bishbulb in #2610 ### EF Core / ancillary stores * **Fix `DurableLocalQueue` to route incoming envelopes to ancillary stores (#2611)** by @jeremydmiller — handler chains targeting an ancillary message store now have their incoming envelopes persisted in that store rather than the main store, fixing a class of "envelope stuck as Incoming forever" bugs. * **Fix `WolverineEnabled` annotation collision in ancillary-store EF Core scenarios** as part of #2618 — `MapWolverineEnvelopeStorage` is now idempotent when called against the same model graph more than once. ### Other improvements * **Fix `MessageBus` clobbering per-message `Envelope.Source`** in #26e38002 — `MessageBus.Send` was overwriting the per-message `Source` with the host's `ServiceName`, masking the originating service in causation tracking. * **Surface upcoming `ServiceLocationPolicy.NotAllowed` v6 default (#2584)** by @jeremydmiller in #2609 — chains that resolve dependencies via service location now log a warning at codegen time, with a clear migration path to the new opt-in `Wolverine.RuntimeCompilation` story for v6. ### CI stabilization * **Several flaky test classes fixed or marked** in #2612 and #2618 — timeout bumps, `IDisposable` → `IAsyncLifetime` conversions, lifecycle cleanup; chronically broken-on-CI classes (`SqliteTests.Transport.multi_tenancy_with_multiple_files`, `CosmosDbTests.LeaderElection.leader_election`, `Wolverine.RabbitMQ.Tests.send_by_topics`, `Wolverine.RabbitMQ.Tests.sending_raw_messages`) tagged `[Trait("Category", "Flaky")]` with header comments documenting the symptom and the real follow-up fix. * **JasperFx 1.28 / RuntimeCompiler 4.5 ambiguity fallout fixed** in #2618 — files importing both `JasperFx.CodeGeneration` and `JasperFx.RuntimeCompiler` now compile cleanly after the `InitializeSynchronously` extension method was moved into `JasperFx.CodeGeneration` upstream. ### Issue-tracker housekeeping * `#2507` (Quartz.Net / TickerQ first-class integration) added to the 6.0 milestone with a phased plan documented on the issue. * `#1577` (cold-start optimization roadmap) updated with a six-phase plan and current-state checklist; phases 1–4 are non-breaking on v5.x and largely landed in this release. **Full Changelog**: JasperFx/wolverine@V5.33.0...V5.34.0 ## 5.33.0 ## What's Changed This release includes a major reliability fix for distributed leader election, a port of the Polecat 2.x event store integration, and several other quality-of-life improvements. ### Highlights * **Fix #2602: Leader split-brain via stale advisory lock** by @jeremydmiller in #2607 — three-layer defensive fix that detects when a Postgres/SQL Server/MySQL/Oracle/SQLite advisory lock has been silently released server-side, steps down the local leader, releases its agents, and triggers a fresh leader election. Adds a new `LeadershipLost` node record type and an `IWolverineObserver.LostLeadership()` hook for monitoring. * **Polecat 2.x event store integration** by @jeremydmiller in #2598 — ports the aggregate handler workflow from Marten so SQL Server-backed projects can use Polecat as a native event store with Wolverine. Requires SQL Server 2025 for the native `JSON` type. * **Fix #2571: Preserve context fields on scheduled-send wrap/unwrap** by @jeremydmiller in #2605 — saga IDs, tenant IDs, and other correlation fields now survive the scheduled-send envelope round-trip. * **gRPC enhancements** by @jeremydmiller in #2565 — middleware weaving, validate convention, user exception mapping, bidirectional streaming, code-first codegen, plus new samples. * **Allow RabbitMQ exchanges to be declared passive** by @jeremydmiller in #2574 ### Other Improvements * Move non-sticky-handlers guard inside the compile lock by @jeremydmiller in #2556 * Add `launchSettings.json` to sample projects by @jeremydmiller in #2600 **Full Changelog**: JasperFx/wolverine@V5.32.1...V5.33.0 ## What's Changed * Update vulnerable OpenTelemetry dependencies by @dmytro-pryvedeniuk in JasperFx/wolverine#2590 * Fix tenant partitioning precedence with inferred grouping by @erdtsieck in JasperFx/wolverine#2581 * Use quoted schema names in SQL statements for RDBMS persistence by @esskar in JasperFx/wolverine#2577 * Skip logging when the count is zero by @Bishbulb in JasperFx/wolverine#2560 * Accept *Async suffix on saga method names (#2578) by @jeremydmiller in JasperFx/wolverine#2593 * Fix ancillary store scheduled messages stuck Incoming forever (#2576) by @jeremydmiller in JasperFx/wolverine#2591 * Add Wolverine.SourceGeneration to slnx (supersedes #2575) by @jeremydmiller in JasperFx/wolverine#2592 * Honor [Consumes] / IAcceptsMetadata on form endpoints by @rmasciarella in JasperFx/wolverine#2589 * Materialize EF domain-event scraper before publishing (#2585) by @jeremydmiller in JasperFx/wolverine#2594 * Process Manager via Handlers sample by @erikshafer in JasperFx/wolverine#2579 * Fix missing ParameterDescriptor for form parameters in API descriptions by @rmasciarella in JasperFx/wolverine#2587 * Allow configuration of the MessageBatchTimeout by @lyall-sc in JasperFx/wolverine#2582 * Fix #2588: durable outbox policy ignored for conventionally-routed senders by @jeremydmiller in JasperFx/wolverine#2596 * Fix #2595: explicit DeliveryOptions.SagaId on saga Start cascades should win by @jeremydmiller in JasperFx/wolverine#2597 * Allow RabbitMQ exchanges to be declared passive by @saithis in JasperFx/wolverine#2574 * Fix NoHandlerForEndpointException during concurrent saga chain compile by @Bishbulb in JasperFx/wolverine#2556 * gRPC: Middleware Weaving, Validate Convention, User Exception Mapping, Bidi Streaming, Code-First Codegen, New Samples by @erikshafer in JasperFx/wolverine#2565 * Add launchSettings.json files to the sample projects by @dmytro-pryvedeniuk in JasperFx/wolverine#2600 * Fix #2571: preserve context fields on scheduled-send wrap unwrap by @jeremydmiller in JasperFx/wolverine#2605 * Add Endpoint.BrokerRole for CritterWatch endpoint display (#2601) by @jeremydmiller in JasperFx/wolverine#2603 * Process Manager via Handlers Cleanup by @erikshafer in JasperFx/wolverine#2604 * Fix #2602: leader split-brain via stale advisory-lock state by @jeremydmiller in JasperFx/wolverine#2607 * Polecat parity port from Wolverine.Marten + Polecat 2.1.0 bump by @jeremydmiller in JasperFx/wolverine#2598 ## New Contributors * @esskar made their first contribution in JasperFx/wolverine#2577 * @rmasciarella made their first contribution in JasperFx/wolverine#2589 * @saithis made their first contribution in JasperFx/wolverine#2574 **Full Changelog**: JasperFx/wolverine@V5.32.1...V5.33.0 Commits viewable in [compare view](http://github.com/jasperfx/wolverine/compare/V5.32.1...V5.39.0). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Summary
HttpChain.fillRequestTypeonly honors[Consumes]/IAcceptsMetadatawhen the endpoint has a body request type and is not a form endpoint (HasRequestType && !IsFormData). Form endpoints fall through andapiDescription.SupportedRequestFormatsstays empty.ASP.NET Core 10's
OpenApiDocumentService.GetFormRequestBodythen defaults toapplication/x-www-form-urlencoded:This silently drops
[Consumes("multipart/form-data")]on file-upload endpoints, so generated clients (Orval, NSwag, Kiota) emitURLSearchParamsbodies instead ofFormData— file uploads break at runtime.Fix
Extend
CreateApiDescriptionso that for endpoints with form-source parameters and no formats already populated,IAcceptsMetadataflows intoSupportedRequestFormats:Semantics
[Consumes]→ no change. Wolverine still emits zero formats; MS OpenAPI still defaults toapplication/x-www-form-urlencoded.[Consumes("multipart/form-data")]→ multipart is added toSupportedRequestFormats; MS OpenAPI no longer adds the urlencoded default (itscount == 0guard); spec correctly emits multipart.Regression test
New test endpoint:
New test:
Fails on
main:Passes after the fix:
Test plan
mainRelated
Independent of #2587 (which fixes a separate
ParameterDescriptornull-deref in the same file). Either can land first.