Repository navigation
GH-4840, GH-4841, GH-4843: sharpen the side-effect AOT roots, guard the HTTP rooting order, run a Balanced native lane - #4847
Merged
Conversation
…he HTTP rooting order, run a Balanced native lane GH-4840: SideEffectAotRoots roots the interfaces that DECLARE Execute/ExecuteAsync, which is the test findMethod itself applies, instead of the castability proxy. Two shapes where the answers differ are now tests. The IL2037 question is measured: the Fisher lane returns a member-less marker side-effect interface and publishes with zero ILC warnings, so no member-less filter is needed. That measurement found a real bug: HandlerChain.PublishedTypes() listed side-effect return types, so PrepopulateRoutingCache closed EmptyMessageRouter<T> over an application-declared side-effect interface at startup and the native image died. A side effect is never routed; PublishedTypes() now skips INotToBeRouted types. GH-4841: the recorded EndpointMetadataProviderTypes form is kept, and its ordering assumption is loud: under codegen write, HttpGraph.BuildFiles refuses by chain name while any endpoint is unbuilt. Static attach is unaffected. GH-4843: the Marten native lane runs DurabilityMode.Balanced and waits for leadership and the durability agent assignment. Measured limit: with the fourteen #4842 router factories stripped, the single node still boots on net9.0 and net10.0, so the lane covers the clustered path without reproducing that failure. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DbTkhs3JxSMCWasTzPdRq
This was referenced Oct 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4840, closes #4841, closes #4843. All three are follow-ups to #4825 / #4828, and all three are about the same thing: a Native AOT rooting block that can be silently short. One PR because the measurement each issue asked for needed the same native lanes, and the one real bug that measurement found cuts across them.
#4840: the side-effect root filter
SideEffectAotRootsnow roots the interfaces that declareExecute/ExecuteAsync(DeclaredOnly), which is literally the testSideEffectPolicy.findMethodapplies on its walk. The castability-to-ISideEffectproxy is gone. Two shapes where the two answers differ are now tests inaot_roots_for_side_effects, and both were red against the old filter before the change:Executewithout itself inheritingISideEffectis rooted.findMethodwalks every interface of the return type, so that is where it finds the method, and the old filter left exactly that metadata unrooted.IWolverineReturnTypeandINotToBeRoutedare now asserted out by name rather than falling out of the filter by accident.The IL2037 question, measured. The Fisher lane now returns a member-less marker side-effect interface (
IAotFisherMarkerOp : IFisherOp) from a handler, which is the shape CoreTests' fixture always had and nothing had ever published. The emitted[DynamicDependency(All, typeof(IAotFisherMarkerOp))]publishes with zero ILC warnings underTreatWarningsAsErrors. ILC counts an interface's own base interfaces as members, soAllover anISideEffectdescendant can never resolve nothing. No "drop member-less roots" filter was added, because there is nothing for it to drop; the lane keeps the fact measured instead of assumed.What the measurement actually found instead is a real bug on
main: the native Fisher lane crashed inPrepopulateRoutingCachewithEmptyMessageRouter<IAotFisherMarkerOp>missing native code.HandlerChain.PublishedTypes()listed every variable a handler creates, side effects included, soAllMessageTypes()handed every side-effect return type to the routing cache warm-up, which closed a router over it reflectively. A side effect is consumed by its policy inside the chain and is never routed; nothing can root a router for it.PublishedTypes()now skipsINotToBeRoutedtypes. Covered bya_side_effect_return_type_is_not_a_published_type(red without the fix) and by the Fisher lane itself, which is green natively with the marker. The library-declaredIFisherOphappened not to trip the same close, which is why no lane saw this before an application-declared interface went through one.#4841: the
EndpointMetadataProviderTypesordering assumptionDecided for the recorded form, deliberately. It cannot drift from the three
tryApplyAsEndpointMetadataProvidercall sites, and the silent-drift failure mode of the declarative form is the one nothing can check. The ordering assumption it makes is now loud: undercodegen write,HttpGraph.BuildFilesrefuses, by chain name, when any chain's endpoint was never built, so "nothing was recorded" can no longer read as "nothing needed rooting". Only underWithinCodegenCommand, becauseAssertPreBuiltTypesExistlegitimately walksBuildFilesbeforeBuildEndpointduring Static attach and emits nothing there. Both directions are tests: the guard fires (red againstmain'sHttpGraph), and the Static-attach path still enumerates. The full codegen drift gate (all eleven projects) regenerates unchanged apart from the Fisher lane's own new handler, so no real application trips it.#4843: a clustered durable app in a native image
The Marten lane now runs
DurabilityMode.Balanced, the first native image in CI to do so. It waits until the node has assumed leadership and has been assigned thewolverinedb://durability agent before serving anything, so election,EvaluateAssignments, theAssignAgentround trip and the agent teardown onStopAsyncare all load-bearing. Green natively, no other change to the lane's pre-gen.The honest limit, measured both ways. With the fourteen #4842 router factories stripped back out, this single Balanced node still boots and passes, on net9.0 and on net10.0. The lane covers the clustered path; it does not reproduce failure 4. That matches the thread: nobody in this repository has observed that close fire in a native image, and the direct construction in #4842 stands on reasoning plus the coverage test, not on a reproduction. The csproj header and the lane say so, so that nobody over-reads a green run. Option 3 from the issue (the static coverage test) remains the thing that actually guards that mechanism.
Gates
wolverine.slnxRelease-f net9.0: clean🤖 Generated with Claude Code
https://claude.ai/code/session_017DbTkhs3JxSMCWasTzPdRq