You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CIPolecat has been red on main since 1dfdd42a1. Bisected to a commit whose entire content is a version-string bump, which cannot be the mechanism — so something reads $(Version) at a distance and I could not find what.
Status: cause localised with high confidence, mechanism unknown. Nothing fixed.
The failure
18 tests fail, all with the same exception, clustered in two namespaces:
[FAILED] PolecatTests.Requirements.using_data_requirements.document_does_not_exist_attribute_happy_path
— Wolverine.Runtime.Routing.IndeterminateRoutesException : Could not determine any valid
subscribers or local handlers for message type PolecatTests.Requirements.EnsureNoDuplicatePcThing
Exception breakdown across the run: 54 IndeterminateRoutesException, 24 RequiredDataMissingException, 24 ShouldAssertException, 1 InvalidOperationException. The supervisor reports 43 passed, 25 passed on retry, 18 failed (25 retries, 27 worker processes) — identically on every red run.
This is a handler-discovery failure: the message types have no chain, so InvokeAsync cannot route them.
The bisect
Six runs, each a workflow_dispatch on a throwaway branch with the tests.yml matrix trimmed to CIPolecat only (one job per run instead of 28). All within the same ~40 minute window, so runner-image and upstream-package drift are held constant.
commit
content
result
runs
7e8f8ba86
GH-3774 — completion continuations honor the metrics-silent tracker
Under a coin-flip null, 3/3 vs 3/3 is p ≈ 1.6%; counting the two organic failures it is 5/5 red at the bumped version. Bounded on the other side by 11 consecutive green CIPolecat runs on main through 3ad7986b6.
NuGet resolution. Restore graphs at the two commits are identical — 112 libraries each, differing only in the version labels on the in-solution project references. There is no WolverineFxPackageReference anywhere; PolecatTests reaches Wolverine purely through ProjectReference. The Polecat package depends on JasperFx/JasperFx.Events only, never on WolverineFx.
Worker crash / infrastructure. No crashed or indeterminate workers, no connection failures, no OOM. The 43 passed / 43 failed-on-attempt-1 split is a whole-worker wipeout, but the worker did not die — every one of its tests threw the same routing exception.
A newly published Polecat. 5.9.1 was published 2026-07-31T22:39, before both the last green and the first red run.
A missing package asymmetry. Both WolverineFx 6.24.3 and 6.24.4 are published on nuget.org.
Does not reproduce locally
Green in every local configuration tried, on macOS/arm64 against the docker-compose SQL Server:
the failing class alone — 14/14
the full CIPolecat shard filter in one process — 86/86
the real supervisor via ./build.sh CIPolecat --disable-test-retry — 86/86
all of the above at 1dfdd42a1, the known-red sha
and again with Polecat pinned to 5.9.1
Leads worth taking first
Something reads $(Version) at runtime. That is the whole puzzle. Both failing areas are source-generator territory (JasperFx.Events.SourceGenerator, StronglyTypedId — note strong_typed_identifiers is among the failures), and Wolverine's runtime codegen is the obvious place an assembly version could leak into type resolution. Start by diffing the generated code between the two versions rather than the source.
Why only in CI. The local/CI gap is real and is worth closing on its own (see below) — whatever differs there is likely the same thing that makes the version visible.
Adjacent finding worth fixing regardless
Polecat is the only floating dependency in Directory.Packages.props:
A warm local cache resolves 5.7.0; CI restores fresh and gets 5.9.1. That is a silent two-minor-version gap between what a developer tests and what CI tests. It is not the cause here — pinning 5.9.1 locally still passes — but it will eventually produce a "works on my machine" that costs someone a day.
The identical 43/25/18 on every red run is not by itself evidence of determinism. With 2 worker processes and a deterministic class partition, any whole-worker failure yields the same split. It took a controlled pass/fail pair at adjacent commits to establish determinism.
Trimming the tests.yml matrix to a single target turns a bisect probe from 28 jobs into 1. Cheap enough to resample properly, which is what separated signal from luck here.
Reproduction harness
Three throwaway branches carry the trimmed-matrix probes, one per bisect point. The repo ruleset blocks branch deletion, so they are still there and are directly re-runnable with gh workflow run tests.yml --ref <branch>:
CIPolecathas been red onmainsince1dfdd42a1. Bisected to a commit whose entire content is a version-string bump, which cannot be the mechanism — so something reads$(Version)at a distance and I could not find what.Status: cause localised with high confidence, mechanism unknown. Nothing fixed.
The failure
18 tests fail, all with the same exception, clustered in two namespaces:
PolecatTests.Requirements.using_data_requirementsPolecatTests.strong_typed_identifiersPolecatTests.handler_actions_with_returned_StartStreamPolecatTests.handler_actions_with_implied_polecat_operationsException breakdown across the run: 54
IndeterminateRoutesException, 24RequiredDataMissingException, 24ShouldAssertException, 1InvalidOperationException. The supervisor reports43 passed, 25 passed on retry, 18 failed (25 retries, 27 worker processes)— identically on every red run.This is a handler-discovery failure: the message types have no chain, so
InvokeAsynccannot route them.The bisect
Six runs, each a
workflow_dispatchon a throwaway branch with thetests.ymlmatrix trimmed toCIPolecatonly (one job per run instead of 28). All within the same ~40 minute window, so runner-image and upstream-package drift are held constant.7e8f8ba8657464ba911dfdd42a1git diffbetween the passing and failing probe branches is one line:Under a coin-flip null, 3/3 vs 3/3 is p ≈ 1.6%; counting the two organic failures it is 5/5 red at the bumped version. Bounded on the other side by 11 consecutive green
CIPolecatruns onmainthrough3ad7986b6.Ruled out (tested, not assumed)
IMessageTrackerparameter and aMessageContext.Trackerproperty; it never touches routing or discovery. This was my initial suspicion purely from filename adjacency (NoHandlerExecutor,NoHandlerContinuation) and it is wrong.DeadLetterMessage.csin the RavenDb and CosmosDb projects, which PolecatTests does not reference.WolverineFxPackageReferenceanywhere;PolecatTestsreaches Wolverine purely throughProjectReference. ThePolecatpackage depends onJasperFx/JasperFx.Eventsonly, never onWolverineFx.43 passed / 43 failed-on-attempt-1split is a whole-worker wipeout, but the worker did not die — every one of its tests threw the same routing exception.WolverineFx6.24.3 and 6.24.4 are published on nuget.org.Does not reproduce locally
Green in every local configuration tried, on macOS/arm64 against the docker-compose SQL Server:
CIPolecatshard filter in one process — 86/86./build.sh CIPolecat --disable-test-retry— 86/861dfdd42a1, the known-red shaPolecatpinned to 5.9.1Leads worth taking first
$(Version)at runtime. That is the whole puzzle. Both failing areas are source-generator territory (JasperFx.Events.SourceGenerator,StronglyTypedId— notestrong_typed_identifiersis among the failures), and Wolverine's runtime codegen is the obvious place an assembly version could leak into type resolution. Start by diffing the generated code between the two versions rather than the source.Adjacent finding worth fixing regardless
Polecatis the only floating dependency inDirectory.Packages.props:A warm local cache resolves 5.7.0; CI restores fresh and gets 5.9.1. That is a silent two-minor-version gap between what a developer tests and what CI tests. It is not the cause here — pinning 5.9.1 locally still passes — but it will eventually produce a "works on my machine" that costs someone a day.
Things that will bite you
CIPolecatis one of three shards.PolecatTestsis split by namespace acrossCIPolecat/CIPolecatWorkflow/CIPolecatSagas(CI: CIAWS and CIPolecat jobs chronically hit the 20-minute execution timeout #3350); the failing namespaces live in theCIPolecatcatch-all. Running the whole project locally is not the same test.43/25/18on every red run is not by itself evidence of determinism. With 2 worker processes and a deterministic class partition, any whole-worker failure yields the same split. It took a controlled pass/fail pair at adjacent commits to establish determinism.tests.ymlmatrix to a single target turns a bisect probe from 28 jobs into 1. Cheap enough to resample properly, which is what separated signal from luck here.Reproduction harness
Three throwaway branches carry the trimmed-matrix probes, one per bisect point. The repo ruleset blocks branch deletion, so they are still there and are directly re-runnable with
gh workflow run tests.yml --ref <branch>:tmp/bisect-gh3774— at7e8f8ba86(green)tmp/bisect-mid— at57464ba91(green)tmp/bisect-tip— at1dfdd42a1(red)They should be deleted once this is closed.
🤖 Generated with Claude Code
https://claude.ai/code/session_013eR4GL278688VhyhrGcttJ