Skip to content

Commit 74d38ec

Browse files
cephalonautoz-agent
andcommitted
Fix: forward child lifecycle events to handle_event_batch in Primary drain
In drain_family_events, only parent_self_events were passed to handle_event_batch. convert_lifecycle_events (inside handle_event_batch) filters by run_id != self_run_id, so it needs child events to inject lifecycle notifications into OrchestrationEventService for the parent conversation. Without this, the parent BlocklistAIController never receives child lifecycle events, causing a regression for local parents with local or remote children. Fix: collect child lifecycle events during the drain loop and extend parent_self_events before the handle_event_batch call in Primary mode. Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent e8d39d6 commit 74d38ec

1 file changed

Lines changed: 20 additions & 5 deletions

File tree

app/src/ai/blocklist/orchestration_event_streamer.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -540,9 +540,10 @@ impl OrchestrationEventStreamer {
540540
///
541541
/// The tracker is passed by value and returned so callers can keep it in
542542
/// `self` state without a borrow conflict against `handle_event_batch`
543-
/// and `killed_run_ids`. The tracker is the sole status writer and emits
544-
/// `ChildStatusChanged` from `observe_child`, so the drain does not
545-
/// re-broadcast lifecycle status here.
543+
/// and `killed_run_ids`. The tracker is the sole status writer for child
544+
/// status and emits `ChildStatusChanged`; child lifecycle events are also
545+
/// forwarded to `handle_event_batch` in Primary mode so the parent's
546+
/// `OrchestrationEventService` receives them for conversation injection.
546547
#[allow(clippy::too_many_arguments)]
547548
fn drain_family_events(
548549
&mut self,
@@ -562,6 +563,13 @@ impl OrchestrationEventStreamer {
562563
.unwrap_or(previous_cursor);
563564

564565
let mut parent_self_events = Vec::new();
566+
// Child lifecycle events are also forwarded to handle_event_batch in
567+
// Primary mode: `convert_lifecycle_events` (inside handle_event_batch)
568+
// filters by run_id != self_run_id, so it picks up child events and
569+
// injects them into OrchestrationEventService for the parent
570+
// conversation. Without this, the parent's BlocklistAIController
571+
// never receives child lifecycle notifications.
572+
let mut child_lifecycle_for_batch = Vec::new();
565573
for event in events {
566574
match classify_family_event(&event, self_run_id) {
567575
FamilyEvent::ParentSelf(event) => parent_self_events.push(event),
@@ -616,6 +624,11 @@ impl OrchestrationEventStreamer {
616624
log::debug!(
617625
"[orch-drain] calling observe_child(Lifecycle) for child_run_id={child_run_id}"
618626
);
627+
// Also collect for handle_event_batch so OrchestrationEventService
628+
// delivers the lifecycle event to the parent conversation.
629+
if mode == FamilyDrainMode::Primary {
630+
child_lifecycle_for_batch.push(event);
631+
}
619632
tracker.observe_child(
620633
&child_run_id,
621634
ChildSignal::Lifecycle(kind),
@@ -651,12 +664,14 @@ impl OrchestrationEventStreamer {
651664

652665
match mode {
653666
FamilyDrainMode::Primary => {
654-
if !parent_self_events.is_empty() || !messages.is_empty() {
667+
let mut events_for_batch = parent_self_events;
668+
events_for_batch.extend(child_lifecycle_for_batch);
669+
if !events_for_batch.is_empty() || !messages.is_empty() {
655670
self.handle_event_batch(
656671
cursor_conversation_id,
657672
self_run_id,
658673
previous_cursor,
659-
parent_self_events,
674+
events_for_batch,
660675
messages,
661676
ctx,
662677
);

0 commit comments

Comments
 (0)