The GH-3521 divergence warning is meant to make a silent first-host-wins application-assembly pin observable, so that a "No routes can be determined" mystery has a breadcrumb. In its current form it does neither reliably: one path can never warn, and the other warns almost always.
This is the Wolverine-side twin of JasperFx/jasperfx#601. Surfaced while diagnosing #3776, where both defects actively pushed the investigation the wrong way.
1. The RememberedApplicationAssembly path cannot warn
WolverineOptions.Assemblies.cs:112-127:
else if (RememberedApplicationAssembly != null)
{
ApplicationAssembly = RememberedApplicationAssembly; // adopts a process-wide pin, silently
}
This adopts an assembly pinned by whichever host started first in the process — exactly the situation the warning exists for — and never calls CheckForDivergentApplicationAssembly. Only the jasperfx.ApplicationAssembly path in ReadJasperFxOptions does.
JasperFx's equivalent already gets this right (JasperFxOptions.cs:171 calls checkForDivergentApplicationAssembly in the same branch), so this looks like a straightforward omission rather than a deliberate difference.
2. On the other path it fires on healthy hosts
Instrumenting a fully green local run of the CIPolecat shard: RegistrationCallingAssembly resolved to Wolverine.SqlServer on 81 of 82 hosts, not to PolecatTests which actually called UseWolverine. The adopted assembly was correctly PolecatTests, so the comparison found a mismatch and raised the warning on all 81 — on a run where nothing was wrong at all.
The cause is determineCallingAssembly (via CaptureRegistrationCallingAssembly): when a Wolverine extension registers on the application's behalf, the first non-System*/Microsoft* frame outside Wolverine belongs to the extension.
Why this is worth fixing rather than tolerating
On #3776 I recorded "zero occurrences of the warning in the failing logs" as grounds for ruling out an application-assembly problem. That was precisely the bug — discovery was scanning xunit.v3.core. The reasoning was void twice over: defect 1 meant that path could not have warned, and defect 2 means a healthy run is full of warnings anyway, so neither presence nor absence carries information.
A diagnostic that cannot be reasoned from in either direction is worse than none, because it invites exactly this mistake.
Suggested direction
- Call
CheckForDivergentApplicationAssembly in the RememberedApplicationAssembly branch, matching JasperFx.
- Make
RegistrationCallingAssembly mean what its name says before relying on it for the comparison — either by skipping Wolverine's own extension assemblies in the walk, or by capturing the registering assembly at the outermost public entry point rather than re-deriving it deep in the registration path.
- Failing that, narrow the trigger so it only fires where the resolution is actually known to be unreliable, rather than on every name mismatch.
Related: #3776, #3777 (which adds IsTestRunnerAssembly and could supply part of the filtering), JasperFx/jasperfx#600, JasperFx/jasperfx#601.
🤖 Generated with Claude Code
https://claude.ai/code/session_013eR4GL278688VhyhrGcttJ
The GH-3521 divergence warning is meant to make a silent first-host-wins application-assembly pin observable, so that a "No routes can be determined" mystery has a breadcrumb. In its current form it does neither reliably: one path can never warn, and the other warns almost always.
This is the Wolverine-side twin of JasperFx/jasperfx#601. Surfaced while diagnosing #3776, where both defects actively pushed the investigation the wrong way.
1. The
RememberedApplicationAssemblypath cannot warnWolverineOptions.Assemblies.cs:112-127:This adopts an assembly pinned by whichever host started first in the process — exactly the situation the warning exists for — and never calls
CheckForDivergentApplicationAssembly. Only thejasperfx.ApplicationAssemblypath inReadJasperFxOptionsdoes.JasperFx's equivalent already gets this right (
JasperFxOptions.cs:171callscheckForDivergentApplicationAssemblyin the same branch), so this looks like a straightforward omission rather than a deliberate difference.2. On the other path it fires on healthy hosts
Instrumenting a fully green local run of the
CIPolecatshard:RegistrationCallingAssemblyresolved toWolverine.SqlServeron 81 of 82 hosts, not toPolecatTestswhich actually calledUseWolverine. The adopted assembly was correctlyPolecatTests, so the comparison found a mismatch and raised the warning on all 81 — on a run where nothing was wrong at all.The cause is
determineCallingAssembly(viaCaptureRegistrationCallingAssembly): when a Wolverine extension registers on the application's behalf, the first non-System*/Microsoft*frame outside Wolverine belongs to the extension.Why this is worth fixing rather than tolerating
On #3776 I recorded "zero occurrences of the warning in the failing logs" as grounds for ruling out an application-assembly problem. That was precisely the bug — discovery was scanning
xunit.v3.core. The reasoning was void twice over: defect 1 meant that path could not have warned, and defect 2 means a healthy run is full of warnings anyway, so neither presence nor absence carries information.A diagnostic that cannot be reasoned from in either direction is worse than none, because it invites exactly this mistake.
Suggested direction
CheckForDivergentApplicationAssemblyin theRememberedApplicationAssemblybranch, matching JasperFx.RegistrationCallingAssemblymean what its name says before relying on it for the comparison — either by skipping Wolverine's own extension assemblies in the walk, or by capturing the registering assembly at the outermost public entry point rather than re-deriving it deep in the registration path.Related: #3776, #3777 (which adds
IsTestRunnerAssemblyand could supply part of the filtering), JasperFx/jasperfx#600, JasperFx/jasperfx#601.🤖 Generated with Claude Code
https://claude.ai/code/session_013eR4GL278688VhyhrGcttJ