Conversation
When the app restarts mid-reflect-loop, ResumeOrchestrationIfPendingAsync collects worker results and sends a synthesis prompt to the orchestrator. For OrchestratorReflect groups, the orchestrator may respond with new @worker blocks (it wants to keep iterating). The previous code used SendPromptAsync (fire-and-forget), so that response was never read — the @worker assignments were silently dropped and the loop stalled forever. Fix: for reflect groups, use SendPromptAndWaitAsync to get the orchestrator's response, parse it for @worker assignments, and if found execute one more bounded dispatch+collect+synthesize round. The OrchestratorCollectionTimeout and ForceCompleteProcessingAsync guards match the normal reflect loop path. Observed: orchestrator emitted @worker:Copilot Cli-worker-1 at 21:19:56 after resume synthesis; worker never received the message; loop stalled until screen lock killed the connection at 21:23. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔍 Multi-Model Code Review — PR #528 (Re-review #3 — v3 sentinel fix)PR: fix: reflect resume drops orchestrator @worker dispatch after restart Status of All Findings
v3 Sentinel Fix VerificationThe fix correctly distinguishes three states of
The staleness check: Minor Observation (🟢)🟢 MINOR —
|
…atch Both existing dispatch paths (SendViaOrchestratorAsync, SendViaOrchestratorReflectAsync) filter the orchestrator from the worker list. The new reflect-resume path in MonitorAndSynthesizeAsync was missing this filter, allowing self-dispatch if the orchestrator's response includes its own name in an @worker block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… completion Race condition in IDLE-DEFER: backgroundTasksChanged fires with shells=0 (PolyPilot confirms all shells done) then a session.idle arrives with shells=2 (stale CLI snapshot generated before completions landed). PolyPilot was starting a fresh 60-min zombie clock on the stale payload, keeping IsProcessing=true indefinitely. Fix: capture pre-idle fingerprint/ticks before calling RefreshDeferredBackgroundTaskTracking. If they were null/0 (backgroundTasksChanged already confirmed empty), treat the idle payload as stale and skip the defer. PolyPilot's real-time tracking is ground truth. Observed in the PROMPT session: agents all completed (agents=0), backgroundTasksChanged showed shells=0, but session.idle fired with shells=2 — session stuck in IsProcessing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ver-seen in IDLE-DEFER null = never seen a backgroundTasksChanged event this turn (initial/reset state) string.Empty = backgroundTasksChanged explicitly confirmed zero tasks non-empty = backgroundTasksChanged reported active tasks with this fingerprint The previous null check caused a false positive: when session.idle arrived with genuine new tasks before backgroundTasksChanged had fired at all, preIdleFingerprint==null && ticks==0 matched the same condition as confirmed- empty, incorrectly treating the idle payload as stale and skipping the defer.
- BackgroundTasksIdleTests: update handler search to match variable binding pattern; replace CompareExchange assertion with RefreshDeferredBackgroundTaskTracking (logic moved there); anchor DoesNotContain to DeferredBackgroundTaskFingerprint field name - MultiAgentRegressionTests: increase block size to 14000 chars to reach the reflect-resume code at the end of the method Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
@workerdispatch after app restart — fixed by usingSendPromptAndWaitAsync+ParseTaskAssignmentsinMonitorAndSynthesizeAsyncavailableWorkersin the reflect resume path included the orchestrator — fixed by filtering it out (matches both existing dispatch paths)session.idlewith a stale background-task payload kept sessions stuck inIsProcessingindefinitely — fixed by detecting whenbackgroundTasksChangedalready confirmed empty before the idle arrivedObserved failure
Orchestrator dispatched
@worker:Copilot Cli-worker-1at21:19:56 UTC, but fire-and-forget synthesis never parsed the response. Worker never received the task. Session timed out 30 min later.PROMPT session:
backgroundTasksChanged shells=0fired, thensession.idle shells=2arrived (stale CLI snapshot). Fresh 60-min zombie clock started, session stuck inIsProcessing=true.Test plan
MonitorAndSynthesize_ReflectResume_WaitsForOrchestratorResponse— verifies await + dispatch wiringSessionIdle_StalePayload_NotDeferredWhenBgTasksAlreadyConfirmedEmpty— verifies staleness guard