diff --git a/src/runtime/dispatchers/orchestration.rs b/src/runtime/dispatchers/orchestration.rs index 99ef7fe..30e1f42 100644 --- a/src/runtime/dispatchers/orchestration.rs +++ b/src/runtime/dispatchers/orchestration.rs @@ -974,7 +974,7 @@ impl Runtime { // Create a new history manager to append the failure event let mut failure_history_mgr = HistoryManager::from_history(&item.history); - failure_history_mgr.append_failed(infra_error.clone()); + failure_history_mgr.append_failed(&item.instance, item.execution_id, infra_error.clone()); // Try to commit the failure event let failure_delta = failure_history_mgr.delta().to_vec(); @@ -1356,7 +1356,7 @@ impl Runtime { } } - history_mgr.append_failed(error.clone()); + history_mgr.append_failed(&item.instance, item.execution_id, error.clone()); let metadata = ExecutionMetadata { status: Some("Failed".to_string()), diff --git a/src/runtime/execution.rs b/src/runtime/execution.rs index c5801c5..1c6d0d0 100644 --- a/src/runtime/execution.rs +++ b/src/runtime/execution.rs @@ -273,7 +273,7 @@ impl Runtime { Self::emit_terminal_cancellation_breadcrumbs(history_mgr, instance, execution_id, &outstanding); // Add failure event last - history_mgr.append_failed(details.clone()); + history_mgr.append_failed(instance, execution_id, details.clone()); // Notify parent if this is a sub-orchestration if let Some((parent_instance, parent_id)) = parent_link { @@ -354,7 +354,7 @@ impl Runtime { Self::emit_terminal_cancellation_breadcrumbs(history_mgr, instance, execution_id, &outstanding); // Add failure event last, and propagate cancellation to outstanding sub-orchestrations. - history_mgr.append_failed(details.clone()); + history_mgr.append_failed(instance, execution_id, details.clone()); for (_schedule_id, child_suffix) in &outstanding.sub_orchestrations { orchestrator_items.push(WorkItem::CancelInstance { diff --git a/src/runtime/state_helpers.rs b/src/runtime/state_helpers.rs index 5c4c45c..8359433 100644 --- a/src/runtime/state_helpers.rs +++ b/src/runtime/state_helpers.rs @@ -167,14 +167,12 @@ impl HistoryManager { } /// Append an OrchestrationFailed event with the next event_id - pub fn append_failed(&mut self, details: crate::ErrorDetails) { + pub fn append_failed(&mut self, instance_id: &str, execution_id: u64, details: crate::ErrorDetails) { let next_id = self.next_event_id(); - // Note: instance_id and execution_id should be set from context - // For now, use placeholder values as this will be set by the caller self.append(Event::with_event_id( next_id, - "", // instance_id should be set by caller - 0, // execution_id should be set by caller + instance_id, + execution_id, None, EventKind::OrchestrationFailed { details }, )); diff --git a/tests/e2e_samples.rs b/tests/e2e_samples.rs index d206e24..733eb6e 100644 --- a/tests/e2e_samples.rs +++ b/tests/e2e_samples.rs @@ -1360,6 +1360,26 @@ async fn sample_cancellation_parent_cascades_to_children_fs() { .await; assert!(ok, "timeout waiting for parent cancel failure"); + // Regression: the terminal OrchestrationFailed event must preserve the + // instance_id/execution_id so telemetry pipelines reading event payloads + // can correlate the cancellation back to the workflow instance. + let parent_hist = store.read("inst-sample-cancel").await.unwrap_or_default(); + let failed = parent_hist + .iter() + .rev() + .find(|e| matches!(&e.kind, EventKind::OrchestrationFailed { .. })) + .expect("parent history should contain OrchestrationFailed"); + assert_eq!( + failed.instance_id, + "inst-sample-cancel", + "OrchestrationFailed must carry the instance_id" + ); + assert_ne!( + failed.execution_id, + 0, + "OrchestrationFailed must carry a non-placeholder execution_id" + ); + // Find child instance (prefix is parent::sub::) and check it was canceled too let mgmt = store.as_management_capability().expect("ProviderAdmin required"); let children: Vec = mgmt