Repository navigation
.NET: Deduplicate fan-out targets so each target runs once - #9432
Merged
Vincent Biret (baywet) merged 2 commits intoOct 16, 2026
Merged
Vincent Biret (baywet) merged 2 commits into
Vincent Biret (baywet) merged 2 commits into
Conversation
FanOutEdgeRunner mapped every index returned by a fan-out target selector to a sink ID and delivered once per entry, so a target selected twice ran twice on the same message. The same happened without a selector when the target list named an executor twice. Drop repeated IDs once they are resolved, keeping the order in which each target is first named.
Ruiqi Wang (RachelWanggg)
temporarily deployed
to
github-app-auth
October 15, 2026 20:45 — with
GitHub Actions
Inactive
Ruiqi Wang (RachelWanggg)
temporarily deployed
to
github-app-auth
October 15, 2026 20:45 — with
GitHub Actions
Inactive
Vincent Biret (baywet)
approved these changes
Oct 16, 2026
Vincent Biret (baywet)
left a comment
Member
There was a problem hiding this comment.
Thanks for the contribution!
Vincent Biret (baywet)
enabled auto-merge
October 16, 2026 10:50
Member
|
depends on #9455 for merge |
Vincent Biret (baywet)
temporarily deployed
to
github-app-auth
October 16, 2026 12:30 — with
GitHub Actions
Inactive
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Oct 16, 2026
This branch was previously deployed
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.
Motivation & Context
A fan-out target selector that returns the same index more than once gets that target run once per entry.
FanOutEdgeRunner.ChaseEdgeAsyncmaps the indices to sink IDs and keeps every entry, so the target appears twice in theDeliveryMappingandMapIntoenqueues the envelope twice into its queue. A selector returning[0, 0]runs target 0 twice on the same message. Routing by tags hits this whenever two tags map to the same executor. With an agent executor as the target, the message appears twice in the agent's prompt, and the agent runs twice if theTurnTokenis routed the same way.Description & Review Guide
FanOutEdgeRunner.ChaseEdgeAsyncdrops repeated target IDs withDistinct(StringComparer.Ordinal)once the IDs are resolved, from the selector or from the target list when there is no selector, and before the envelope-target andCanHandlechecks.Distinctkeeps the first occurrence, so delivery order follows the order in which each target is first named. This is the change Python: Deduplicate fan-out selection results so each target runs once #8739 made to the PythonFanOutEdgeRunner, which also covers both paths.AddSwitchis unaffected, sinceSwitchBuilderalready collects its case indices in aHashSet<int>. No public API change.Three tests in
EdgeRunnerTests.cs, all failing onmain:Test_FanOutEdgeRunner_DeduplicatesAssignedTargetsAsync: a selector returning[1, 0, 1]over["executor2", "executor3"]delivers toexecutor3, thenexecutor2, once each. Onmainthe deliveries are["executor3", "executor2", "executor3"].Test_FanOutEdgeRunner_DeduplicatesRepeatedSinksAsync: no selector, target list["executor2", "executor2"], one delivery. Onmainthere are two.Test_FanOutEdge_TargetAssignedTwiceRunsOnceAsync: a workflow built withAddFanOutEdge<string>and a selector returning[0, 0]runs the target executor once. Onmainit runs twice.Local verification on macOS arm64, .NET SDK 10.0.402,
dotnet test --project tests/<project> -f net10.0, branch on top of deecbcb. Themaincolumn is this branch withFanOutEdgeRunner.csreverted tomain.Microsoft.Agents.AI.<name>.UnitTests)mainSkip)Skip)EdgeRunnerTestsonly: removing theDistinctfails all three new tests. Applying it only when there is no selector fails the two selector tests. Applying it only to selector results failsTest_FanOutEdgeRunner_DeduplicatesRepeatedSinksAsync. Rebuilding the list in target-list order, or sorting it, failsTest_FanOutEdgeRunner_DeduplicatesAssignedTargetsAsync.dotnet format <csproj> --verify-no-changespasses forMicrosoft.Agents.AI.WorkflowsandMicrosoft.Agents.AI.Workflows.UnitTests.dotnet build src/Microsoft.Agents.AI.Workflows --warnaserrorpasses for net10.0, net9.0, net8.0, netstandard2.0 and net472 with 0 warnings.net472tests (Windows only), integration tests, and unit test projects that do not depend onMicrosoft.Agents.AI.Workflows.Related Issue
Fixes #9431
AI Assistance
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix). A workflow keeps the label and title prefix in sync automatically.