@@ -1681,6 +1681,7 @@ private async Task SendViaOrchestratorReflectAsync(string groupId, List<string>
16811681 }
16821682
16831683 var workerNames = members . Where ( m => m != orchestratorName ) . ToList ( ) ;
1684+ var dispatchedWorkers = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
16841685
16851686 try
16861687 {
@@ -1705,7 +1706,7 @@ private async Task SendViaOrchestratorReflectAsync(string groupId, List<string>
17051706 }
17061707 else
17071708 {
1708- planPrompt = BuildReplanPrompt ( reflectState . LastEvaluation ?? "Continue iterating." , workerNames , prompt ) ;
1709+ planPrompt = BuildReplanPrompt ( reflectState . LastEvaluation ?? "Continue iterating." , workerNames , prompt , group . RoutingContext ) ;
17091710 }
17101711
17111712 var planResponse = await SendPromptAndWaitAsync ( orchestratorName , planPrompt , ct ) ;
@@ -1760,10 +1761,15 @@ private async Task SendViaOrchestratorReflectAsync(string groupId, List<string>
17601761 var workerTasks = assignments . Select ( a => ExecuteWorkerAsync ( a . WorkerName , a . Task , prompt , ct ) ) ;
17611762 var results = await Task . WhenAll ( workerTasks ) ;
17621763
1764+ // Track which workers have been dispatched across all iterations
1765+ foreach ( var a in assignments )
1766+ dispatchedWorkers . Add ( a . WorkerName ) ;
1767+
17631768 // Phase 4: Synthesize + Evaluate
17641769 InvokeOnUI ( ( ) => OnOrchestratorPhaseChanged ? . Invoke ( groupId , OrchestratorPhase . Synthesizing , iterDetail ) ) ;
17651770
1766- var synthEvalPrompt = BuildSynthesisWithEvalPrompt ( prompt , results . ToList ( ) , reflectState ) ;
1771+ var synthEvalPrompt = BuildSynthesisWithEvalPrompt ( prompt , results . ToList ( ) , reflectState ,
1772+ group . RoutingContext , dispatchedWorkers , workerNames ) ;
17671773
17681774 // Use dedicated evaluator session if configured, otherwise orchestrator self-evaluates
17691775 string evaluatorName = reflectState . EvaluatorSessionName ?? orchestratorName ;
@@ -1783,14 +1789,26 @@ private async Task SendViaOrchestratorReflectAsync(string groupId, List<string>
17831789 var evaluatorModel = GetEffectiveModel ( evaluatorName ) ;
17841790 var trend = reflectState . RecordEvaluation ( reflectState . CurrentIteration , score , rationale , evaluatorModel ) ;
17851791
1786- // Check if evaluator says complete
1787- if ( evalResponse . Contains ( "[[GROUP_REFLECT_COMPLETE]]" , StringComparison . OrdinalIgnoreCase ) || score >= 0.9 )
1792+ // Check if evaluator says complete — but only if all workers have participated
1793+ var allWorkersDispatched = workerNames . All ( w => dispatchedWorkers . Contains ( w ) ) ;
1794+ if ( ( evalResponse . Contains ( "[[GROUP_REFLECT_COMPLETE]]" , StringComparison . OrdinalIgnoreCase ) || score >= 0.9 )
1795+ && allWorkersDispatched )
17881796 {
17891797 reflectState . GoalMet = true ;
17901798 reflectState . IsActive = false ;
17911799 AddOrchestratorSystemMessage ( orchestratorName , $ "✅ { reflectState . BuildCompletionSummary ( ) } (score: { score : F1} )") ;
17921800 break ;
17931801 }
1802+ else if ( ! allWorkersDispatched )
1803+ {
1804+ var missing = workerNames . Where ( w => ! dispatchedWorkers . Contains ( w ) ) . ToList ( ) ;
1805+ Debug ( $ "Reflection: overriding completion — workers not yet dispatched: { string . Join ( ", " , missing ) } ") ;
1806+ reflectState . LastEvaluation = $ "Not all workers have participated yet. Missing: { string . Join ( ", " , missing ) } . " +
1807+ "Dispatch to the remaining workers before completing." ;
1808+ AddOrchestratorSystemMessage ( orchestratorName ,
1809+ $ "🔄 Overriding completion — { string . Join ( ", " , missing ) } haven't participated yet.") ;
1810+ continue ;
1811+ }
17941812
17951813 reflectState . LastEvaluation = rationale ;
17961814 if ( trend == Models . QualityTrend . Degrading )
@@ -1800,14 +1818,30 @@ private async Task SendViaOrchestratorReflectAsync(string groupId, List<string>
18001818 {
18011819 synthesisResponse = await SendPromptAndWaitAsync ( orchestratorName , synthEvalPrompt , ct ) ;
18021820
1803- // Check completion sentinel
1804- if ( synthesisResponse . Contains ( "[[GROUP_REFLECT_COMPLETE]]" , StringComparison . OrdinalIgnoreCase ) )
1821+ // Check completion sentinel — but only if all workers have participated
1822+ var allWorkersDispatched = workerNames . All ( w => dispatchedWorkers . Contains ( w ) ) ;
1823+ if ( synthesisResponse . Contains ( "[[GROUP_REFLECT_COMPLETE]]" , StringComparison . OrdinalIgnoreCase )
1824+ && allWorkersDispatched )
18051825 {
18061826 reflectState . GoalMet = true ;
18071827 reflectState . IsActive = false ;
18081828 AddOrchestratorSystemMessage ( orchestratorName , $ "✅ { reflectState . BuildCompletionSummary ( ) } ") ;
18091829 break ;
18101830 }
1831+ else if ( synthesisResponse . Contains ( "[[GROUP_REFLECT_COMPLETE]]" , StringComparison . OrdinalIgnoreCase )
1832+ && ! allWorkersDispatched )
1833+ {
1834+ // Override premature completion — not all workers have participated
1835+ var missing = workerNames . Where ( w => ! dispatchedWorkers . Contains ( w ) ) . ToList ( ) ;
1836+ Debug ( $ "Reflection: overriding [[GROUP_REFLECT_COMPLETE]] — workers not yet dispatched: { string . Join ( ", " , missing ) } ") ;
1837+ reflectState . LastEvaluation = $ "Not all workers have participated yet. Missing: { string . Join ( ", " , missing ) } . " +
1838+ "Dispatch to the remaining workers before completing." ;
1839+ AddOrchestratorSystemMessage ( orchestratorName ,
1840+ $ "🔄 Overriding completion — { string . Join ( ", " , missing ) } haven't participated yet.") ;
1841+ reflectState . RecordEvaluation ( reflectState . CurrentIteration , 0.3 ,
1842+ reflectState . LastEvaluation , GetEffectiveModel ( orchestratorName ) ) ;
1843+ continue ;
1844+ }
18111845
18121846 // Extract evaluation for next iteration
18131847 reflectState . LastEvaluation = ExtractIterationEvaluation ( synthesisResponse ) ;
@@ -1895,11 +1929,30 @@ private async Task SendViaOrchestratorReflectAsync(string groupId, List<string>
18951929 }
18961930 }
18971931
1898- private string BuildSynthesisWithEvalPrompt ( string originalPrompt , List < WorkerResult > results , ReflectionCycle state )
1932+ private string BuildSynthesisWithEvalPrompt ( string originalPrompt , List < WorkerResult > results , ReflectionCycle state ,
1933+ string ? routingContext = null , HashSet < string > ? dispatchedWorkers = null , List < string > ? allWorkers = null )
18991934 {
19001935 var sb = new System . Text . StringBuilder ( ) ;
19011936 sb . Append ( BuildSynthesisPrompt ( originalPrompt , results ) ) ;
19021937 sb . AppendLine ( ) ;
1938+ if ( ! string . IsNullOrEmpty ( routingContext ) )
1939+ {
1940+ sb . AppendLine ( "## Work Routing (from team definition)" ) ;
1941+ sb . AppendLine ( routingContext ) ;
1942+ sb . AppendLine ( ) ;
1943+ }
1944+ // Show which workers have/haven't participated
1945+ if ( dispatchedWorkers != null && allWorkers != null )
1946+ {
1947+ var missing = allWorkers . Where ( w => ! dispatchedWorkers . Contains ( w ) ) . ToList ( ) ;
1948+ if ( missing . Count > 0 )
1949+ {
1950+ sb . AppendLine ( "### ⚠️ Worker Participation" ) ;
1951+ sb . AppendLine ( $ "The following workers have NOT yet been dispatched: **{ string . Join ( ", " , missing ) } **") ;
1952+ sb . AppendLine ( "You MUST include `[[NEEDS_ITERATION]]` and dispatch to them before marking complete." ) ;
1953+ sb . AppendLine ( ) ;
1954+ }
1955+ }
19031956 sb . AppendLine ( $ "## Evaluation Check (Iteration { state . CurrentIteration } /{ state . MaxIterations } )") ;
19041957 sb . AppendLine ( $ "**Goal:** { state . Goal } ") ;
19051958 sb . AppendLine ( ) ;
@@ -1931,20 +1984,31 @@ private string BuildSynthesisWithEvalPrompt(string originalPrompt, List<WorkerRe
19311984 return sb . ToString ( ) ;
19321985 }
19331986
1934- private string BuildReplanPrompt ( string lastEvaluation , List < string > workerNames , string originalPrompt )
1987+ private string BuildReplanPrompt ( string lastEvaluation , List < string > workerNames , string originalPrompt ,
1988+ string ? routingContext = null )
19351989 {
19361990 var sb = new System . Text . StringBuilder ( ) ;
1991+ sb . AppendLine ( "You are a DISPATCHER ONLY. You do NOT have tools. You CANNOT write code, read files, or run commands yourself." ) ;
1992+ sb . AppendLine ( "Your ONLY job is to write @worker/@end blocks that assign work to your workers." ) ;
1993+ sb . AppendLine ( ) ;
19371994 sb . AppendLine ( "## Previous Iteration Evaluation" ) ;
19381995 sb . AppendLine ( lastEvaluation ) ;
19391996 sb . AppendLine ( ) ;
19401997 sb . AppendLine ( "## Original Request (context)" ) ;
19411998 sb . AppendLine ( originalPrompt ) ;
19421999 sb . AppendLine ( ) ;
2000+ if ( ! string . IsNullOrEmpty ( routingContext ) )
2001+ {
2002+ sb . AppendLine ( "## Work Routing (from team definition)" ) ;
2003+ sb . AppendLine ( routingContext ) ;
2004+ sb . AppendLine ( ) ;
2005+ }
19432006 sb . AppendLine ( $ "Available workers ({ workerNames . Count } ):") ;
19442007 foreach ( var w in workerNames )
19452008 sb . AppendLine ( $ " - '{ w } ' (model: { GetEffectiveModel ( w ) } )") ;
19462009 sb . AppendLine ( ) ;
19472010 sb . AppendLine ( "Assign refined tasks using `@worker:name` / `@end` blocks to address the gaps identified above." ) ;
2011+ sb . AppendLine ( "You MUST produce at least one @worker block. NEVER attempt to do the work yourself." ) ;
19482012 return sb . ToString ( ) ;
19492013 }
19502014
0 commit comments