From 2c972a063aefb60b3842a14bf8a6e58c00f785ed Mon Sep 17 00:00:00 2001 From: David Kaya Date: Tue, 24 Mar 2026 23:47:31 +0100 Subject: [PATCH] fix: tighten handoff routing behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Eryx.AgentHost/Services/AgentInstructionComposer.cs | 4 +++- .../src/Eryx.AgentHost/Services/CopilotAgentBundle.cs | 9 ++++++++- .../Eryx.AgentHost/Services/HandoffWorkflowGuidance.cs | 2 ++ .../AgentInstructionComposerTests.cs | 4 +++- .../Eryx.AgentHost.Tests/HandoffWorkflowGuidanceTests.cs | 1 + src/shared/domain/pattern.ts | 2 +- tests/shared/pattern.test.ts | 3 ++- 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs b/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs index f47a1a9..ed0f0bc 100644 --- a/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs +++ b/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs @@ -48,7 +48,9 @@ internal static class AgentInstructionComposer ? """ You are the routing gate for this handoff workflow. Your job is to classify the request and hand it off to the most appropriate specialist as soon as you know who should own the substantive work. - Do not perform the specialist's implementation, design, or execution work yourself once a specialist is appropriate. + For any substantive task, your next meaningful action must be the actual handoff rather than a plain-text promise to delegate later. + Do not inspect files, call tools, draft the implementation, or produce the final user-facing answer yourself once a specialist is appropriate. + Do not claim that you handed work off unless you actually executed the handoff. Only answer directly if the user is asking for pure triage or a minimal clarification that must happen before delegation. """ : """ diff --git a/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs b/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs index c6c3117..b555597 100644 --- a/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs +++ b/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs @@ -136,10 +136,17 @@ internal sealed class CopilotAgentBundle : IAsyncDisposable continue; } + string handoffReason = string.Equals( + route.TargetAgentId, + topology.EntryAgentId, + StringComparison.Ordinal) + ? HandoffWorkflowGuidance.CreateReturnReason(targetDefinition) + : HandoffWorkflowGuidance.CreateForwardReason(targetDefinition); + builder = builder.WithHandoff( sourceAgent, targetAgent, - HandoffWorkflowGuidance.CreateForwardReason(targetDefinition)); + handoffReason); } return builder.Build(); diff --git a/sidecar/src/Eryx.AgentHost/Services/HandoffWorkflowGuidance.cs b/sidecar/src/Eryx.AgentHost/Services/HandoffWorkflowGuidance.cs index e7089e3..e668de2 100644 --- a/sidecar/src/Eryx.AgentHost/Services/HandoffWorkflowGuidance.cs +++ b/sidecar/src/Eryx.AgentHost/Services/HandoffWorkflowGuidance.cs @@ -11,6 +11,8 @@ internal static class HandoffWorkflowGuidance If another agent should do the substantive work, perform an actual handoff instead of answering as though the handoff already happened. Do not claim that you delegated unless you actually executed the handoff. The triage agent should route to the best specialist promptly once ownership is clear. + In a specialist workflow, the triage agent should hand off before inspecting files, calling tools, or drafting the substantive implementation. + Do not narrate a handoff in plain text without executing the handoff itself. Specialists should complete the substantive work after handoff and only hand control back when the task needs re-routing, broader coordination, or is outside their specialty. """; } diff --git a/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs b/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs index fdac684..2a11b81 100644 --- a/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs +++ b/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs @@ -71,7 +71,9 @@ public sealed class AgentInstructionComposerTests string instructions = AgentInstructionComposer.Compose(pattern, triage, agentIndex: 0); Assert.Contains("routing gate", instructions, StringComparison.OrdinalIgnoreCase); - Assert.Contains("Do not perform the specialist", instructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Do not inspect files", instructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("actual handoff", instructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Do not claim that you handed work off", instructions, StringComparison.OrdinalIgnoreCase); } [Fact] diff --git a/sidecar/tests/Eryx.AgentHost.Tests/HandoffWorkflowGuidanceTests.cs b/sidecar/tests/Eryx.AgentHost.Tests/HandoffWorkflowGuidanceTests.cs index 3e20376..4fbd59f 100644 --- a/sidecar/tests/Eryx.AgentHost.Tests/HandoffWorkflowGuidanceTests.cs +++ b/sidecar/tests/Eryx.AgentHost.Tests/HandoffWorkflowGuidanceTests.cs @@ -12,6 +12,7 @@ public sealed class HandoffWorkflowGuidanceTests Assert.Contains("explicit handoffs", instructions, StringComparison.OrdinalIgnoreCase); Assert.Contains("Do not claim that you delegated", instructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Do not narrate a handoff", instructions, StringComparison.OrdinalIgnoreCase); Assert.Contains("Specialists should complete the substantive work", instructions, StringComparison.OrdinalIgnoreCase); } diff --git a/src/shared/domain/pattern.ts b/src/shared/domain/pattern.ts index 927c347..5a55f2d 100644 --- a/src/shared/domain/pattern.ts +++ b/src/shared/domain/pattern.ts @@ -427,7 +427,7 @@ export function createBuiltinPatterns(timestamp: string): PatternDefinition[] { name: 'Triage', description: 'Routes the request to the right specialist.', instructions: - 'You triage requests and must hand them off to the most appropriate specialist. Do not do the specialist work yourself once the right owner is clear.', + 'You triage requests and must hand them off to the most appropriate specialist. For any substantive task, hand off before inspecting files, calling tools, or drafting the implementation yourself. Do not claim that you delegated unless you actually executed the handoff.', model: defaultModels.gpt54, reasoningEffort: 'medium', }, diff --git a/tests/shared/pattern.test.ts b/tests/shared/pattern.test.ts index 7e9adc8..65d8429 100644 --- a/tests/shared/pattern.test.ts +++ b/tests/shared/pattern.test.ts @@ -91,7 +91,8 @@ describe('pattern validation', () => { const handoff = createBuiltinPatterns(BUILTIN_TIMESTAMP).find((pattern) => pattern.mode === 'handoff'); expect(handoff).toBeDefined(); - expect(handoff?.agents[0].instructions).toContain('Do not do the specialist work yourself'); + expect(handoff?.agents[0].instructions).toContain('hand off before inspecting files'); + expect(handoff?.agents[0].instructions).toContain('Do not claim that you delegated'); expect(handoff?.agents[1].instructions).toContain('own the substantive answer'); expect(handoff?.agents[2].instructions).toContain('own the substantive answer'); });