diff --git a/.github/workflows/server-parity.yml b/.github/workflows/server-parity.yml index 354b3bf48..09d06a05f 100644 --- a/.github/workflows/server-parity.yml +++ b/.github/workflows/server-parity.yml @@ -45,6 +45,7 @@ on: - tests/Unit/ServerParityRecordLocalActivityContractTest.mjs - tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs - tests/Unit/ServerParityActivityCancellationAckContractTest.mjs + - tests/Unit/ServerParityCancellationScopesContractTest.mjs - tests/Unit/ServerParityServiceCatalogContractTest.mjs - tests/Unit/ServerParityWorkflowImportContractTest.mjs - tests/Unit/ServerParityWaterlineImportContractTest.mjs @@ -1803,7 +1804,7 @@ jobs: bash scripts/ci/run-composer.sh --rm -v "$PWD:/source" -w /source -- \ install --working-dir=scripts/conformance/server-parity --no-interaction --no-progress docker run --rm --user=1000:1000 --entrypoint node -v "$PWD:/source:ro" -w /source \ - "$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParitySdkDirectionLanesTest.mjs tests/Unit/ServerParityCodecDeadlineCellsTest.mjs tests/Unit/ServerParityCooperativeContractTest.mjs tests/Unit/ServerParityChildCancellationContractTest.mjs tests/Unit/ServerParityScheduleContractTest.mjs tests/Unit/ServerParityVisibilityContractTest.mjs tests/Unit/ServerParityAdmissionContractTest.mjs tests/Unit/ServerParityRoleTokenContractTest.mjs tests/Unit/ServerParityWorkerDeregistrationContractTest.mjs tests/Unit/ServerParitySearchAttributeContractTest.mjs tests/Unit/ServerParityWorkerListingContractTest.mjs tests/Unit/ServerParityWorkerReplacementContractTest.mjs tests/Unit/ServerParityPatchDeploymentContractTest.mjs tests/Unit/ServerParityWaitingHistoryContractTest.mjs tests/Unit/ServerParityTaskRetryContractTest.mjs tests/Unit/ServerParityPublishedPatchContractTest.mjs tests/Unit/ServerParityPublishedPatchTransportTest.mjs tests/Unit/ServerParityRecordFileTest.mjs tests/Unit/ServerParityFailureLocationTest.mjs tests/Unit/ServerParityFailureExcerptTest.mjs tests/Unit/ServerParityLateWorkflowCompletionContractTest.mjs tests/Unit/ServerParityStorageTestContractTest.mjs tests/Unit/ServerParitySchedulePoliciesContractTest.mjs tests/Unit/ServerParitySignalsBeforeWorkerContractTest.mjs tests/Unit/ServerParityScheduleCatalogContractTest.mjs tests/Unit/ServerParityOperatorMetricsContractTest.mjs tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs tests/Unit/ServerParityRecordLocalActivityContractTest.mjs tests/Unit/ServerParityActivityCancellationAckContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParityWorkflowImportContractTest.mjs tests/Unit/ServerParityWaterlineImportContractTest.mjs tests/Unit/ServerParitySystemMetricsContractTest.mjs tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs tests/Unit/ServerParityPrometheusMetricsContractTest.mjs tests/Unit/ServerParityPayloadCleanupContractTest.mjs tests/Unit/ServerParityRepairPassContractTest.mjs tests/Unit/ServerParityClusterInfoContractTest.mjs tests/Unit/ServerParityNamespaceDeleteContractTest.mjs tests/Unit/ServerParityMessageStreamsContractTest.mjs tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs tests/Unit/ServerParityUpdateValidationContractTest.mjs tests/Unit/ServerParityFixtureSelectionTest.mjs tests/Unit/ServerParityUpdateValidationRetryTest.mjs tests/Unit/ServerParityKilledAuthorPollTest.mjs tests/Unit/ServerParityImagePullTest.mjs tests/Unit/ServerParityWorkerSessionsContractTest.mjs tests/Unit/ServerParityWorkerSessionActivitiesContractTest.mjs > parity-evidence/comparator-tests.txt + "$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParitySdkDirectionLanesTest.mjs tests/Unit/ServerParityCodecDeadlineCellsTest.mjs tests/Unit/ServerParityCooperativeContractTest.mjs tests/Unit/ServerParityChildCancellationContractTest.mjs tests/Unit/ServerParityScheduleContractTest.mjs tests/Unit/ServerParityVisibilityContractTest.mjs tests/Unit/ServerParityAdmissionContractTest.mjs tests/Unit/ServerParityRoleTokenContractTest.mjs tests/Unit/ServerParityWorkerDeregistrationContractTest.mjs tests/Unit/ServerParitySearchAttributeContractTest.mjs tests/Unit/ServerParityWorkerListingContractTest.mjs tests/Unit/ServerParityWorkerReplacementContractTest.mjs tests/Unit/ServerParityPatchDeploymentContractTest.mjs tests/Unit/ServerParityWaitingHistoryContractTest.mjs tests/Unit/ServerParityTaskRetryContractTest.mjs tests/Unit/ServerParityPublishedPatchContractTest.mjs tests/Unit/ServerParityPublishedPatchTransportTest.mjs tests/Unit/ServerParityRecordFileTest.mjs tests/Unit/ServerParityFailureLocationTest.mjs tests/Unit/ServerParityFailureExcerptTest.mjs tests/Unit/ServerParityLateWorkflowCompletionContractTest.mjs tests/Unit/ServerParityStorageTestContractTest.mjs tests/Unit/ServerParitySchedulePoliciesContractTest.mjs tests/Unit/ServerParitySignalsBeforeWorkerContractTest.mjs tests/Unit/ServerParityScheduleCatalogContractTest.mjs tests/Unit/ServerParityOperatorMetricsContractTest.mjs tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs tests/Unit/ServerParityRecordLocalActivityContractTest.mjs tests/Unit/ServerParityActivityCancellationAckContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParityWorkflowImportContractTest.mjs tests/Unit/ServerParityWaterlineImportContractTest.mjs tests/Unit/ServerParitySystemMetricsContractTest.mjs tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs tests/Unit/ServerParityPrometheusMetricsContractTest.mjs tests/Unit/ServerParityPayloadCleanupContractTest.mjs tests/Unit/ServerParityRepairPassContractTest.mjs tests/Unit/ServerParityClusterInfoContractTest.mjs tests/Unit/ServerParityNamespaceDeleteContractTest.mjs tests/Unit/ServerParityMessageStreamsContractTest.mjs tests/Unit/ServerParityCancellationScopesContractTest.mjs tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs tests/Unit/ServerParityUpdateValidationContractTest.mjs tests/Unit/ServerParityFixtureSelectionTest.mjs tests/Unit/ServerParityUpdateValidationRetryTest.mjs tests/Unit/ServerParityKilledAuthorPollTest.mjs tests/Unit/ServerParityImagePullTest.mjs tests/Unit/ServerParityWorkerSessionsContractTest.mjs tests/Unit/ServerParityWorkerSessionActivitiesContractTest.mjs > parity-evidence/comparator-tests.txt docker run --rm --user=1000:1000 --cpus=1 --memory=256m --memory-swap=256m \ --network=none --entrypoint node -v "$PWD:/source:ro" -w /source \ "$image" --test tests/Unit/ServerParityCodecDeadlineLoadTest.mjs > parity-evidence/codec-load-tests.txt diff --git a/docs/rust-server-port.md b/docs/rust-server-port.md index dccafa9e7..c1773dec0 100644 --- a/docs/rust-server-port.md +++ b/docs/rust-server-port.md @@ -82,12 +82,12 @@ Updated October 24, 2026. ([#737](https://github.com/durable-workflow/server/issues/737)). No expectation is edited to make native pass. -- **Parity surface.** Native serves 140 of the 175 API route methods in +- **Parity surface.** Native serves 143 of the 175 API route methods in published 2.11.1's route table, with message streams, update validation, - activity cancellation acknowledgement, worker sessions, and embedded v2 - and Waterline v1 workflow import. An execution test sends each one through - the native router, and the ones still answering - `501 rust_capability_not_implemented` must match + activity cancellation acknowledgement, worker sessions, workflow-task + cancellation scopes, and embedded v2 and Waterline v1 workflow import. An + execution test sends each one through the native router, and the ones + still answering `501 rust_capability_not_implemented` must match [`unserved-php-routes.v2.11.1.txt`](../rust/tests/fixtures/unserved-php-routes.v2.11.1.txt). Unlike published PHP, native's embedded v2 import refuses a bundle whose rows reference rows outside it, links only runs of one namespace, and reads @@ -139,7 +139,8 @@ any other unknown path or method answers PHP's own 404 or 405 - **Worker protocol features.** - Local activities (prepare, checkpoint, recover, heartbeat, control, outcome and cancellation acknowledgement). - - Cancellation scopes. + - Cancellation scope checkpoints. (Open, prepare and deliver are served, + with the `cancellation-scopes` fixture.) - (Worker sessions are served: create, heartbeat, close, list and detail, with the `worker-sessions` and `worker-session-activities` fixtures.) - (Activity cancellation acknowledgement is served: diff --git a/rust/src/runtime/activity_cancellation_ack.rs b/rust/src/runtime/activity_cancellation_ack.rs index f89e65662..dfdebeee0 100644 --- a/rust/src/runtime/activity_cancellation_ack.rs +++ b/rust/src/runtime/activity_cancellation_ack.rs @@ -103,7 +103,12 @@ store_methods! { { return Err(refused("activity_cancellation_acknowledgement_fence_mismatch")); } - if DB::optional_string(run, "cancellation_request_command_id")?.as_deref() != Some(request_id) { + // The run's request, or a scope request the run recorded. + let (_, facts) = Self::scope_run(&mut tx, &run_id).await?; + let events = Self::scope_events(&mut tx, &run_id).await?; + if facts.request_id.as_deref() != Some(request_id) + && !events.iter().any(|event| event.kind == "CancellationScopeRequested" && event.payload["request_id"] == request_id) + { return Err(refused("cancellation_request_mismatch")); } let Some((cancellation_event, cancellation)) = @@ -117,7 +122,14 @@ store_methods! { { return Err(refused("activity_cancellation_snapshot_mismatch")); } - let Some(context) = Self::cooperative_context(&mut tx, &run_id, request_id).await? else { + // `ActivityCancellationContext::forEvent`: a scoped fence's accepted + // scope context, or the run request's. + let Some(context) = events.iter().find(|event| event.id == cancellation_event) + .and_then(|event| super::cancellation_scopes::cancellation_facts(&events, &facts, event)) + .filter(|(request, _, _)| request == request_id) + .and_then(|(_, root_request_id, deadline)| Some(CooperativeContext { root_request_id, + deadline: chrono::DateTime::parse_from_rfc3339(&deadline).ok()?.with_timezone(&chrono::Utc) })) + else { return Err(refused("cancellation_context_not_recorded")); }; if let Some((event, _)) = Self::acknowledgement_event(&mut tx, &run_id, &cancellation_event, attempt_id).await? { @@ -156,20 +168,21 @@ store_methods! { execution_id: &str, attempt_id: &str, ) -> Result> { - let Some(request_id) = DB::optional_string(run, "cancellation_request_command_id")? else { - return Ok(None); - }; + // The execution's original fence, whichever request recorded it. let run_id = DB::string(run, "id")?; - let Some((cancellation_event, _)) = - Self::activity_cancellation_event(tx, &run_id, &request_id, execution_id, attempt_id).await? + let (_, facts) = Self::scope_run(tx, &run_id).await?; + let events = Self::scope_events(tx, &run_id).await?; + let Some(cancelled) = events.iter().find(|event| event.kind == "ActivityCancelled" && event.payload["activity_execution_id"] == execution_id) + .filter(|event| event.payload["activity_attempt_id"] == attempt_id) else { return Ok(None); }; - let Some(context) = Self::cooperative_context(tx, &run_id, &request_id).await? else { + let Some((request_id, root_request_id, deadline)) = super::cancellation_scopes::cancellation_facts(&events, &facts, cancelled) else { return Ok(None); }; - let mut receipt = json!({"request_id":request_id,"root_request_id":context.root_request_id, - "cleanup_deadline_at":micros(context.deadline),"cancellation_history_event_id":cancellation_event, + let cancellation_event = cancelled.id.clone(); + let mut receipt = json!({"request_id":request_id,"root_request_id":root_request_id, + "cleanup_deadline_at":deadline,"cancellation_history_event_id":cancellation_event, "callback_state":"unknown","history_event_id":null,"acknowledged_at":null,"received_after_deadline":null}); if let Some((event, payload)) = Self::acknowledgement_event(tx, &run_id, &cancellation_event, attempt_id).await? { receipt["callback_state"] = payload["callback_state"].clone(); @@ -216,26 +229,6 @@ store_methods! { } Ok(None) } - - /// The request's root and original cleanup deadline, from its - /// `CooperativeCancellationRequested` event. - async fn cooperative_context(tx: &mut Transaction<'_, DB>, run_id: &str, request_id: &str) -> Result> { - let Some(requested) = Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND workflow_command_id=$2 AND event_type='CooperativeCancellationRequested' ORDER BY sequence LIMIT 1") - .bind(run_id).bind(request_id).fetch_optional(&mut **tx).await? - else { - return Ok(None); - }; - let cancellation = &DB::document_row(&requested, "payload")?["cancellation"]; - let Some(deadline) = cancellation["cleanup_deadline_at"].as_str() - .and_then(|value| chrono::DateTime::parse_from_rfc3339(value).ok()) - .map(|value| value.with_timezone(&chrono::Utc)) - else { - return Ok(None); - }; - let root_request_id = cancellation["lineage"].as_array().and_then(|lineage| lineage.first()) - .and_then(|root| root["request_id"].as_str()).unwrap_or(request_id).to_owned(); - Ok(Some(CooperativeContext { root_request_id, deadline })) - } } struct CooperativeContext { diff --git a/rust/src/runtime/cancellation_scopes.rs b/rust/src/runtime/cancellation_scopes.rs new file mode 100644 index 000000000..41b7dcf0d --- /dev/null +++ b/rust/src/runtime/cancellation_scopes.rs @@ -0,0 +1,2503 @@ +//! Workflow-task cancellation scopes, as published Server 2.11 serves them +//! (`CancellationScopeController` over Workflow 2.10's portable scope opening +//! and delivery, `CancellationScopeRequests` and `CancellationScopeDelivery`). +//! +//! A scope is a node of a per-run tree rooted at the implicit `root` scope, +//! kept only in history: `CancellationScopeOpened` records it at a durable +//! command sequence, completion commands join it by `cancellation_scope_id`, +//! a run cancellation request records `CancellationScopeRequested` for every +//! unshielded descendant of root, and a worker holding a claim prepares a +//! scope's delivery (freezing its subtree inventory) and delivers it (fencing +//! every member, then recording the barrier). Scope delivery never closes the +//! run, releases the claim or creates a task. +//! +//! Native delivers activity and timer members. Scoped waits and children +//! remain refused at completion, and a scope that holds one (only possible in +//! history written by PHP before a takeover) is refused explicitly with +//! `cancellation_scope_delivery_unavailable`, as are parallel and selection +//! boundaries below the next sequence. +use super::store::{capabilities, id, now}; +use super::transaction::Transaction; +use super::{RuntimeError, refuse}; +use axum::http::StatusCode; +use chrono::{DateTime, SecondsFormat, Utc}; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; +use std::collections::BTreeSet; +use std::sync::Arc; + +pub(super) const ROOT: &str = "root"; +const SCOPE_SCHEMA: &str = "durable-workflow.cancellation-scope/v1"; +const REQUEST_SCHEMA: &str = "durable-workflow.cancellation-scope-request/v1"; +const PREPARATION_SCHEMA: &str = "durable-workflow.cancellation-scope-preparation/v5"; +const DELIVERY_SCHEMA: &str = "durable-workflow.cancellation-scope-delivery/v1"; +const SCOPED_CONTEXT_SCHEMA: &str = "durable-workflow.scoped-cancellation-context/v1"; +const ACTIVITY_SCOPE_SCHEMA: &str = "durable-workflow.activity-scope-cancellation/v1"; +const TIMER_SCOPE_SCHEMA: &str = "durable-workflow.scoped-timer-cancellation/v1"; +/// PHP's default `command_batch_size` structural limit. +const SPAN_LIMIT: i64 = 1000; +/// The command types whose `cancellation_scope_id` records membership. +const MEMBER_COMMANDS: &[&str] = &["schedule_activity", "start_timer", "start_child_workflow", "open_signal_wait", "open_condition_wait"]; +/// The command types the admission guard lets carry a scope. +const SCOPED_COMMANDS: &[&str] = + &["schedule_activity", "start_timer", "start_child_workflow", "prepare_local_activity", "open_signal_wait", "open_condition_wait"]; +/// `WorkflowStepHistory::WORKFLOW_STEP_EVENT_TYPES`. +const STEP_EVENTS: &[&str] = &[ + "CancellationScopeOpened", "ActivityScheduled", "ActivityStarted", "ActivityHeartbeatRecorded", "ActivityRetryScheduled", + "ActivityCompleted", "ActivityFailed", "ActivityCancelled", "ActivityTimedOut", "ChildWorkflowScheduled", "ChildRunStarted", + "ChildRunCompleted", "ChildRunFailed", "ChildRunCancelled", "ChildRunTerminated", "ServiceCallStarted", "ServiceCallCompleted", + "ServiceCallFailed", "ServiceCallCancelled", "WorkflowContinuedAsNew", "ConditionWaitOpened", "ConditionWaitSatisfied", + "ConditionWaitTimedOut", "SignalWaitOpened", "SignalApplied", "MemoUpserted", "SearchAttributesUpserted", "SideEffectRecorded", + "VersionMarkerRecorded", "TimerScheduled", "TimerFired", "TimerCancelled", +]; +/// `CooperativeCancellationDelivery::RESOLUTION_TYPES`. +const RESOLUTION_EVENTS: &[&str] = &[ + "ActivityCompleted", "ActivityFailed", "ActivityCancelled", "ActivityTimedOut", "TimerFired", "TimerCancelled", + "ConditionWaitSatisfied", "ConditionWaitTimedOut", "SignalApplied", "ChildRunCompleted", "ChildRunFailed", "ChildRunCancelled", + "ChildRunTerminated", "ServiceCallCompleted", "ServiceCallFailed", "ServiceCallCancelled", "SelectionResolved", + "SelectionOperationCancelled", +]; +const OPERATION_EVENTS: &[&str] = + &["ActivityScheduled", "TimerScheduled", "ChildWorkflowScheduled", "SignalWaitOpened", "ConditionWaitOpened"]; + +type Result = std::result::Result; + +/// A scope engine refusal; the routes answer it with their refusal shape. +fn no(reason: &'static str) -> RuntimeError { + refuse(StatusCode::CONFLICT, reason) +} + +/// PHP's `toISOString`: UTC with microseconds. +pub(super) fn iso(at: DateTime) -> String { + at.to_rfc3339_opts(SecondsFormat::Micros, true) +} + +fn instant(value: &Value) -> Option> { + DateTime::parse_from_rfc3339(value.as_str()?).ok().map(|at| at.with_timezone(&Utc)) +} + +/// PHP's default `json_encode`, as its descriptor hashes read it: `/` and +/// every non-ASCII character escaped. Objects keep the order given. +fn php_json(value: &Value, out: &mut String) { + use std::fmt::Write; + match value { + Value::String(text) => { + out.push('"'); + for character in text.chars() { + match character { + '"' => out.push_str("\\\""), + '\\' => out.push_str("\\\\"), + '/' => out.push_str("\\/"), + '\u{8}' => out.push_str("\\b"), + '\u{c}' => out.push_str("\\f"), + '\n' => out.push_str("\\n"), + '\r' => out.push_str("\\r"), + '\t' => out.push_str("\\t"), + character if character.is_ascii() && (character as u32) >= 0x20 => out.push(character), + character => { + for unit in character.encode_utf16(&mut [0; 2]) { + write!(out, "\\u{unit:04x}").expect("string write"); + } + } + } + } + out.push('"'); + } + Value::Array(items) => { + out.push('['); + for (index, item) in items.iter().enumerate() { + if index > 0 { + out.push(','); + } + php_json(item, out); + } + out.push(']'); + } + other => out.push_str(&other.to_string()), + } +} + +/// `hash('sha256', json_encode($facts))` over a list of facts. Parallel +/// metadata objects arrive pre-encoded in PHP's key order. +fn descriptor_hash(facts: &[Value], encoded_tail: Option<&str>) -> String { + let mut encoded = String::from("["); + for (index, fact) in facts.iter().enumerate() { + if index > 0 { + encoded.push(','); + } + php_json(fact, &mut encoded); + } + if let Some(tail) = encoded_tail { + encoded.push(','); + encoded.push_str(tail); + } + encoded.push(']'); + Sha256::digest(encoded.as_bytes()).iter().map(|byte| format!("{byte:02x}")).collect() +} + +/// `ParallelChildGroup::metadataPathFromPayload`, encoded as PHP's +/// `json_encode` writes it (each entry's keys in PHP's order). +fn parallel_path_json(payload: &Value) -> String { + fn single(payload: &Value) -> Option> { + let group = payload["parallel_group_id"].as_str()?; + let kind = payload["parallel_group_kind"].as_str().map(str::to_owned).or_else(|| { + [("parallel-activities:", "activity"), ("parallel-calls:", "mixed"), ("parallel-timers:", "timer"), + ("parallel-children:", "child"), ("select-calls:", "mixed")] + .iter() + .find(|(prefix, _)| group.starts_with(prefix)) + .map(|(_, kind)| (*kind).to_owned()) + })?; + let mode = payload["parallel_group_mode"] + .as_str() + .filter(|mode| !mode.is_empty()) + .map(str::to_owned) + .or_else(|| group.starts_with("select-calls:").then(|| "select".to_owned())); + let base = payload["parallel_group_base_sequence"].as_i64()?; + let size = payload["parallel_group_size"].as_i64().filter(|size| *size >= 1)?; + let index = payload["parallel_group_index"].as_i64()?; + let select = mode.as_deref() == Some("select"); + let key = &payload["selection_member_key"]; + if select && !(key.is_string() && key.as_str() != Some("") || key.is_i64()) { + return None; + } + let mut fields = vec![("parallel_group_id", json!(group)), ("parallel_group_kind", json!(kind))]; + if select { + fields.push(("parallel_group_mode", json!("select"))); + } + fields.extend([("parallel_group_base_sequence", json!(base)), ("parallel_group_size", json!(size)), + ("parallel_group_index", json!(index))]); + if select { + fields.push(("selection_member_key", key.clone())); + } + for field in ["selection_member_index", "selection_member_base_sequence", "selection_member_size"] { + if let Some(value) = payload[field].as_i64() { + fields.push((field, json!(value))); + } + } + if let Some(kind) = payload["selection_member_kind"].as_str().filter(|kind| !kind.is_empty()) { + fields.push(("selection_member_kind", json!(kind))); + } + Some(fields) + } + let mut path: Vec> = + payload["parallel_group_path"].as_array().into_iter().flatten().filter_map(single).collect(); + if path.is_empty() { + path.extend(single(payload)); + } + let mut encoded = String::from("["); + for (index, entry) in path.iter().enumerate() { + if index > 0 { + encoded.push(','); + } + encoded.push('{'); + for (position, (key, value)) in entry.iter().enumerate() { + if position > 0 { + encoded.push(','); + } + php_json(&json!(key), &mut encoded); + encoded.push(':'); + php_json(value, &mut encoded); + } + encoded.push('}'); + } + encoded.push(']'); + encoded +} + +/// One history event of a run, as the scope engine reads it. The payload +/// is shared, so a cached history is cheap to hand out again. +#[derive(Clone, Debug)] +pub(super) struct Event { + pub(super) id: String, + pub(super) sequence: i64, + pub(super) kind: String, + pub(super) payload: Arc, + pub(super) command_id: Option, +} + +/// The history one transaction has read for one run. It lives in the +/// transaction, which holds the transition lock, so it ends with the commit +/// or rollback. History in a transaction only grows by appended sequences: +/// the in-place payload update (`attribute_worker_terminal`) and the deletes +/// (retention pruning, namespace deletion) clear this, so a later read only +/// fetches rows past the last sequence. +pub(super) struct HistoryCache { + run_id: String, + events: Arc>, +} + +/// A recorded scope, from its `CancellationScopeOpened` event. +#[derive(Clone, Debug)] +pub(super) struct Scope { + pub(super) id: String, + pub(super) parent: String, + pub(super) shield: bool, + pub(super) sequence: i64, + pub(super) event_id: String, +} + +/// The run columns the engine reads. +#[derive(Clone, Debug)] +pub(super) struct RunFacts { + pub(super) id: String, + pub(super) instance: String, + pub(super) status: String, + pub(super) request_id: Option, + pub(super) execution_deadline: Option>, + pub(super) run_deadline: Option>, +} + +impl RunFacts { + fn terminal(&self) -> bool { + !matches!(self.status.as_str(), "pending" | "running" | "waiting") + } +} + +/// `CancellationScopeHistory::forRun`: the run's scopes in sequence order. +/// A parent is recorded before its child, so the tree has no cycles. +pub(super) fn scopes(events: &[Event], run_id: &str) -> Result> { + let mut found: Vec = Vec::new(); + for event in events.iter().filter(|event| event.kind == "CancellationScopeOpened") { + let payload: &Value = &event.payload; + let (Some(id), Some(parent), Some(shield), Some(sequence)) = ( + payload["scope_id"].as_str(), + payload["parent_scope_id"].as_str(), + payload["shield_parent"].as_bool(), + payload["sequence"].as_i64(), + ) else { + return Err(no("cancellation_scope_history_invalid")); + }; + let parent_scope = found.iter().find(|scope| scope.id == parent); + if payload["schema"] != SCOPE_SCHEMA + || payload["workflow_run_id"] != run_id + || id.is_empty() + || id == ROOT + || found.iter().any(|scope| scope.id == id || scope.sequence == sequence) + || parent.is_empty() + || sequence < 1 + || (parent != ROOT && parent_scope.is_none_or(|scope| scope.sequence >= sequence)) + { + return Err(no("cancellation_scope_history_invalid")); + } + found.push(Scope { id: id.to_owned(), parent: parent.to_owned(), shield, sequence, event_id: event.id.clone() }); + } + Ok(found) +} + +fn find<'a>(scopes: &'a [Scope], id: &str) -> Option<&'a Scope> { + scopes.iter().find(|scope| scope.id == id) +} + +/// `CancellationScopeHistory::ancestry`: root first. +fn ancestry(scopes: &[Scope], scope_id: &str) -> Result> { + let mut chain = Vec::new(); + let mut current = scope_id.to_owned(); + while current != ROOT { + let scope = find(scopes, ¤t).ok_or_else(|| no("cancellation_scope_not_recorded"))?; + chain.push(current.clone()); + current = scope.parent.clone(); + } + chain.push(ROOT.to_owned()); + chain.reverse(); + Ok(chain) +} + +/// `CancellationScopeHistory::isRecordedBefore`: a scope owns only later +/// operations; omission means root. +pub(super) fn recorded_before(scopes: &[Scope], scope_id: &str, sequence: i64) -> bool { + sequence >= 1 && (scope_id == ROOT || find(scopes, scope_id).is_some_and(|scope| scope.sequence < sequence)) +} + +fn assert_scope(scopes: &[Scope], scope_id: &str) -> Result<()> { + if scope_id == ROOT { + return Err(no("root_scope_requires_run_cancellation")); + } + find(scopes, scope_id).map(|_| ()).ok_or_else(|| no("cancellation_scope_not_recorded")) +} + +/// The scope's accepted `CancellationScopeRequested` event. +fn accepted<'a>(events: &'a [Event], scope_id: &str) -> Result> { + let mut matching = events.iter().filter(|event| event.kind == "CancellationScopeRequested" && event.payload["scope_id"] == scope_id); + let first = matching.next(); + if matching.next().is_some() { + return Err(no("cancellation_scope_request_history_invalid")); + } + Ok(first) +} + +/// `CancellationScopeRequests::context`: the accepted scoped context. +fn context(events: &[Event], scopes: &[Scope], run_id: &str, scope_id: &str) -> Result> { + assert_scope(scopes, scope_id)?; + let Some(event) = accepted(events, scope_id)? else { + return Ok(None); + }; + let payload: &Value = &event.payload; + let context = &payload["cancellation"]; + if payload["schema"] != REQUEST_SCHEMA + || payload["workflow_run_id"] != run_id + || !context.is_object() + || payload.get("parent_scope_id").is_none() + || context_request(context) != payload["request_id"].as_str() + || last_lineage(context)["scope_id"] != scope_id + { + return Err(no("cancellation_scope_request_history_invalid")); + } + Ok(Some(context.clone())) +} + +fn last_lineage(context: &Value) -> &Value { + context["lineage"].as_array().and_then(|lineage| lineage.last()).unwrap_or(&Value::Null) +} + +fn context_request(context: &Value) -> Option<&str> { + last_lineage(context)["request_id"].as_str() +} + +fn context_deadline(context: &Value) -> Option> { + instant(&last_lineage(context)["cleanup_deadline_at"]) +} + +/// The run request's `CancellationContext`, from its requested event. +fn run_context<'a>(events: &'a [Event], run: &RunFacts) -> Option<&'a Value> { + let request = run.request_id.as_deref()?; + events + .iter() + .find(|event| event.kind == "CooperativeCancellationRequested" && event.command_id.as_deref() == Some(request)) + .map(|event| &event.payload["cancellation"]) + .filter(|context| context.is_object()) +} + +/// `ScopedCancellationContext::fromRunContext` for a root request. +fn scoped_from_run(context: &Value) -> Value { + let deadline = context["cleanup_deadline_at"].clone(); + let mut root = context.clone(); + root["request_id"] = context["root_request_id"].clone(); + root["parent_request_id"] = Value::Null; + root["lineage"] = json!([context["lineage"][0].clone()]); + let lineage: Vec = context["lineage"] + .as_array() + .into_iter() + .flatten() + .map(|entry| { + let mut entry = entry.clone(); + entry["scope_id"] = json!(ROOT); + entry["cleanup_deadline_at"] = deadline.clone(); + entry + }) + .collect(); + json!({"schema": SCOPED_CONTEXT_SCHEMA, "root_context": root, "lineage": lineage}) +} + +/// `ScopedCancellationContext::forDescendant`. +fn for_descendant(parent: &Value, request_id: &str, run: &RunFacts, scope_id: &str) -> Value { + let mut context = parent.clone(); + let deadline = last_lineage(parent)["cleanup_deadline_at"].clone(); + if let Some(lineage) = context["lineage"].as_array_mut() { + lineage.push(json!({"request_id": request_id, "workflow_instance_id": run.instance, "workflow_run_id": run.id, + "scope_id": scope_id, "cleanup_deadline_at": deadline})); + } + context +} + +/// `CancellationScopeRequests::authority`: the earliest of the run's +/// deadlines, its request's budget and every requested ancestor's budget. +#[derive(Clone, Copy, Debug)] +pub(super) struct Authority { + active: bool, + deadline: Option>, +} + +impl Authority { + fn deadline_at(&self) -> Option { + self.deadline.map(iso) + } + fn usable(&self) -> Result { + match (self.active, self.deadline_at()) { + (true, Some(deadline)) => Ok(deadline), + _ => Err(no("cancellation_scope_authority_expired")), + } + } +} + +fn authority(run: &RunFacts, events: &[Event], scopes: &[Scope], scope_id: &str) -> Result { + let chain = ancestry(scopes, scope_id)?; + assert_scope(scopes, scope_id)?; + let mut ceilings = vec![run.execution_deadline, run.run_deadline, run_context(events, run).and_then(|context| instant(&context["cleanup_deadline_at"]))]; + for ancestor in chain.iter().filter(|ancestor| *ancestor != ROOT) { + ceilings.push(context(events, scopes, &run.id, ancestor)?.as_ref().and_then(context_deadline)); + } + let deadline = ceilings.into_iter().flatten().min(); + Ok(Authority { active: !run.terminal() && deadline.is_none_or(|deadline| now() < deadline), deadline }) +} + +/// The unshielded transitive descendants of a scope, in sequence order. +fn descendants<'a>(scopes: &'a [Scope], scope_id: &str) -> Result> { + if scope_id != ROOT && find(scopes, scope_id).is_none() { + return Err(no("cancellation_scope_not_recorded")); + } + let mut included: BTreeSet<&str> = BTreeSet::from([scope_id]); + let mut found = Vec::new(); + for scope in scopes { + if scope.id == scope_id || scope.shield || !included.contains(scope.parent.as_str()) { + continue; + } + included.insert(scope.id.as_str()); + found.push(scope); + } + Ok(found) +} + +/// An operation event's scope: the flat key, else its descriptor's, else +/// root. `None` when the two disagree or are not strings. +fn membership(event: &Event) -> Option { + let nested = match event.kind.as_str() { + "ActivityScheduled" => Some("activity"), + "TimerScheduled" => Some("timer"), + "ChildWorkflowScheduled" => Some("child_workflow"), + _ => None, + }; + let descriptor = nested.and_then(|key| event.payload.get(key)).cloned().unwrap_or_else(|| json!({})); + let flat = event.payload.get("cancellation_scope_id"); + let inner = descriptor.get("cancellation_scope_id"); + let address = match (flat, inner) { + (Some(flat), _) => flat.as_str()?.to_owned(), + (None, Some(inner)) => inner.as_str()?.to_owned(), + (None, None) => ROOT.to_owned(), + }; + if inner.is_some_and(|inner| inner != address.as_str()) { + return None; + } + Some(address) +} + +/// `CancellationScopeDelivery::activityMembers`: each scheduled activity of +/// the scope itself, with its cancellation descriptor hash. +fn activity_members(events: &[Event], scope_id: &str) -> Result> { + let mut members = Vec::new(); + let mut ids = BTreeSet::new(); + let mut sequences = BTreeSet::new(); + for event in events.iter().filter(|event| event.kind == "ActivityScheduled") { + let payload: &Value = &event.payload; + let activity = &payload["activity"]; + let nested = activity["cancellation_scope_id"].as_str().unwrap_or(ROOT); + let flat = payload["cancellation_scope_id"].as_str().unwrap_or(nested); + if flat != scope_id && nested != scope_id { + continue; + } + if flat != scope_id || nested != scope_id { + return Err(no("cancellation_scope_delivery_membership_mismatch")); + } + let (Some(id), Some(sequence)) = (payload["activity_execution_id"].as_str(), payload["sequence"].as_i64()) else { + return Err(no("cancellation_scope_activity_history_invalid")); + }; + if id.is_empty() || activity["id"] != id || sequence < 1 || !ids.insert(id.to_owned()) || !sequences.insert(sequence) { + return Err(no("cancellation_scope_activity_history_invalid")); + } + let hash = descriptor_hash( + &[json!(scope_id), json!(event.id), activity.get("cancellation_policy").cloned().unwrap_or(json!("try_cancel")), + payload.get("local_activity").cloned().unwrap_or(json!(false)), payload.get("execution_mode").cloned().unwrap_or(Value::Null), + activity.get("schedule_to_close_deadline_at").cloned().unwrap_or(Value::Null)], + None, + ); + members.push(json!({"sequence": sequence, "activity_execution_id": id, "descriptor_hash": hash})); + } + Ok(members) +} + +/// `ScopedTimerCancellation::members`: each timer of the scope, fired or +/// cancelled ones included, without shielded cleanup timers. +fn timer_members(events: &[Event], scope_id: &str) -> Result> { + let mut members = Vec::new(); + let mut ids = BTreeSet::new(); + let mut sequences: Vec<(i64, Option)> = Vec::new(); + for event in events.iter().filter(|event| event.kind == "TimerScheduled") { + let payload: &Value = &event.payload; + let descriptor = payload.get("timer").cloned().unwrap_or_else(|| json!({})); + if !descriptor.is_object() { + return Err(no("cancellation_scope_timer_history_invalid")); + } + let nested = descriptor["cancellation_scope_id"].as_str().unwrap_or(ROOT); + let address = payload["cancellation_scope_id"].as_str().unwrap_or(nested); + if address != scope_id && nested != scope_id { + continue; + } + if address != scope_id || (descriptor.get("cancellation_scope_id").is_some() && nested != scope_id) { + return Err(no("cancellation_scope_delivery_membership_mismatch")); + } + let kind = payload["timer_kind"].as_str().map(str::to_owned); + let (Some(id), Some(sequence), Some(delay), Some(fire_at)) = ( + payload["timer_id"].as_str(), + payload["sequence"].as_i64(), + payload["delay_seconds"].as_i64(), + payload["fire_at"].as_str(), + ) else { + return Err(no("cancellation_scope_timer_history_invalid")); + }; + let repeated = sequences.iter().find(|(seen, _)| *seen == sequence); + if id.is_empty() || sequence < 1 || delay < 0 || fire_at.is_empty() || !ids.insert(id.to_owned()) + || repeated.is_some_and(|(_, seen)| !matches!(kind.as_deref(), Some("signal_timeout" | "condition_timeout")) || seen != &kind) + || instant(&payload["fire_at"]).is_none_or(|at| iso(at) != fire_at) + { + return Err(no("cancellation_scope_timer_history_invalid")); + } + sequences.push((sequence, kind)); + if payload.get("cancellation_cleanup").is_some() { + continue; + } + let field = |key: &str| payload.get(key).cloned().unwrap_or(Value::Null); + let hash = descriptor_hash( + &[json!(scope_id), json!(event.id), json!(sequence), json!(id), json!(delay), json!(fire_at), field("timer_kind"), + field("condition_wait_id"), field("condition_wait_occurrence_id"), field("signal_wait_id")], + Some(¶llel_path_json(payload)), + ); + members.push(json!({"sequence": sequence, "timer_id": id, "descriptor_hash": hash})); + } + Ok(members) +} + +/// Native delivers activity and timer members only. +fn refuse_other_members(events: &[Event], scope_id: &str) -> Result<()> { + let other = events.iter().any(|event| { + matches!(event.kind.as_str(), "SignalWaitOpened" | "ConditionWaitOpened" | "ChildWorkflowScheduled") + && membership(event).as_deref() == Some(scope_id) + }); + if other { Err(no("cancellation_scope_delivery_unavailable")) } else { Ok(()) } +} + +/// `CancellationScopeDescendants::members`: each unshielded descendant's +/// request and own members, frozen with the preparation. +fn descendant_members(events: &[Event], scopes: &[Scope], run: &RunFacts, scope_id: &str, authority_deadline: DateTime) -> Result> { + let mut members = Vec::new(); + for scope in descendants(scopes, scope_id)? { + let request = accepted(events, &scope.id)?; + let context = context(events, scopes, &run.id, &scope.id)?; + let parent = context_parent(events, scopes, run, &scope.parent)?; + let (Some(request), Some(context), Some(parent)) = (request, context, parent) else { + return Err(no("cancellation_scope_descendant_request_history_invalid")); + }; + if context["root_context"]["root_request_id"] != parent["root_context"]["root_request_id"] + || request.payload["parent_scope_id"] != scope.parent.as_str() + { + return Err(no("cancellation_scope_descendant_request_history_invalid")); + } + let mut deadline = authority_deadline; + for ancestor in ancestry(scopes, &scope.id)? { + if let Some(ceiling) = accepted(events, &ancestor)?.and_then(|event| context_deadline(&event.payload["cancellation"])) { + deadline = deadline.min(ceiling); + } + } + refuse_other_members(events, &scope.id)?; + members.push(json!({"scope_id": scope.id, "parent_scope_id": scope.parent, "scope_history_event_id": scope.event_id, + "request_history_event_id": request.id, "request_id": context_request(&context), + "propagation_history_event_id": request.id, "authority_deadline_at": iso(deadline), "cancellation": context, + "activity_members": activity_members(events, &scope.id)?, "timer_members": timer_members(events, &scope.id)?, + "wait_members": [], "child_members": []})); + } + Ok(members) +} + +/// A descendant's parent context; a root parent has none here, since a +/// descendant of a prepared scope always has a scoped parent. +fn context_parent(events: &[Event], scopes: &[Scope], run: &RunFacts, parent: &str) -> Result> { + context(events, scopes, &run.id, parent) +} + +/// `CancellationScopeDelivery::prepared` and `recorded`: the scope's one +/// preparation or delivery marker. +fn marker<'a>(events: &'a [Event], scopes: &[Scope], scope_id: &str, kind: &str) -> Result> { + assert_scope(scopes, scope_id)?; + let mut matching = events.iter().filter(|event| event.kind == kind && event.payload["scope_id"] == scope_id); + let first = matching.next(); + if matching.next().is_some() || first.is_some() && accepted(events, scope_id)?.is_none() { + return Err(no("cancellation_scope_delivery_history_invalid")); + } + Ok(first) +} + +fn prepared<'a>(events: &'a [Event], scopes: &[Scope], scope_id: &str) -> Result> { + marker(events, scopes, scope_id, "CancellationScopeDeliveryPrepared") +} + +fn recorded<'a>(events: &'a [Event], scopes: &[Scope], scope_id: &str) -> Result> { + marker(events, scopes, scope_id, "CancellationScopeDelivered") +} + +fn same_boundary(payload: &Value, boundary: &Boundary) -> bool { + payload["sequence"] == boundary.sequence + && payload["call_kind"] == boundary.kind.as_str() + && payload["sequence_span"] == boundary.span + && payload["operation_sequence"] == json!(boundary.operation_sequence) + && payload["operation_sequence_span"] == boundary.operation_span +} + +/// `replaysOriginalOperation`: the command at this sequence was recorded in +/// this scope before the preparation. +fn replays_original(events: &[Event], preparation: &Event, scope_id: &str, sequence: i64) -> bool { + events.iter().any(|event| { + event.sequence < preparation.sequence + && OPERATION_EVENTS.contains(&event.kind.as_str()) + && event.payload["sequence"] == sequence + && membership(event).as_deref() == Some(scope_id) + }) +} + +/// `positionReserved`: an undelivered preparation owns its exact boundary. +fn position_reserved(events: &[Event], scopes: &[Scope], sequence: i64, scope_id: Option<&str>) -> Result { + for event in events.iter().filter(|event| event.kind == "CancellationScopeDeliveryPrepared") { + let scope = event.payload["scope_id"].as_str().filter(|scope| !scope.is_empty()) + .ok_or_else(|| no("cancellation_scope_preparation_history_invalid"))?; + let preparation = prepared(events, scopes, scope)?.ok_or_else(|| no("cancellation_scope_preparation_history_invalid"))?; + if preparation.payload["sequence"] == sequence + && recorded(events, scopes, scope)?.is_none() + && scope_id.is_none_or(|scope_id| !replays_original(events, preparation, scope_id, sequence)) + { + return Ok(true); + } + } + Ok(false) +} + +/// `CancellationScopeDelivery::admissionRefusal`: a prepared scope, or an +/// unshielded scope under one, admits no new member; an undelivered +/// preparation reserves its boundary for every command. +pub(super) fn admission_refusal(events: &[Event], scopes: &[Scope], scope_id: &str, sequence: i64) -> Result> { + if scope_id != ROOT { + let mut address = scope_id.to_owned(); + while let Some(scope) = find(scopes, &address) { + if let Some(preparation) = prepared(events, scopes, &address)? { + return Ok((!replays_original(events, preparation, scope_id, sequence)).then_some("operation_scope_cancellation_prepared")); + } + if scope.shield { + break; + } + address = scope.parent.clone(); + } + } + Ok(position_reserved(events, scopes, sequence, Some(scope_id))?.then_some("operation_cancellation_delivery_reserved")) +} + +/// `validateCancellationScopeMembership` over a batch starting at `start`. +pub(super) fn membership_refusal(events: &[Event], scopes: &[Scope], commands: &[&Value], start: i64) -> Result> { + for command in commands { + if let Some(scope) = command["cancellation_scope_id"].as_str() + && command["type"].as_str().is_some_and(|kind| MEMBER_COMMANDS.contains(&kind)) + && !recorded_before(scopes, scope, start) + { + return Ok(Some("operation_scope_not_recorded")); + } + } + for (offset, command) in commands.iter().enumerate() { + let scope = command["cancellation_scope_id"].as_str().unwrap_or(ROOT); + if let Some(refusal) = admission_refusal(events, scopes, scope, start + offset as i64)? { + return Ok(Some(refusal)); + } + } + Ok(None) +} + +/// Whether a command carries a scope the admission guard reads. +pub(super) fn guard_reason(commands: &[Value], protocol_supported: bool) -> (Option<&'static str>, Vec<&'static str>) { + let mut reason = None; + let mut unavailable = Vec::new(); + if !protocol_supported { + reason = Some("cancellation_scope_membership_unavailable"); + unavailable.push("request_protocol"); + } + for command in commands { + if command.get("cancellation_scope_id").is_some() + && !command["type"].as_str().is_some_and(|kind| SCOPED_COMMANDS.contains(&kind)) + { + reason = Some("invalid_cancellation_scope_command"); + } + } + (reason, unavailable) +} + +/// A delivery boundary named by a worker. +#[derive(Clone, Debug)] +pub(super) struct Boundary { + sequence: i64, + kind: String, + span: i64, + operation_sequence: Option, + operation_span: i64, +} + +/// `CooperativeCancellationDelivery::validateBoundarySyntax`. +fn boundary_syntax(boundary: &Boundary) -> Option<&'static str> { + let shaped = matches!( + boundary.kind.as_str(), + "activity" | "local_activity" | "timer" | "condition" | "signal" | "child" | "service_operation" | "parallel" | "selection_handle" + ); + (boundary.sequence < 1 + || boundary.span < 1 + || boundary.operation_span < 1 + || boundary.sequence > i64::MAX - boundary.span + || boundary.span > SPAN_LIMIT + || boundary.operation_span > SPAN_LIMIT + || !shaped + || (boundary.kind != "parallel" && boundary.span != 1) + || ((boundary.kind == "selection_handle") != boundary.operation_sequence.is_some()) + || (boundary.kind != "selection_handle" && boundary.operation_span != 1)) + .then_some("invalid_cancellation_delivery") +} + +fn local_activity(event: &Event) -> bool { + event.payload["local_activity"] == true || event.payload["execution_mode"] == "local" +} + +/// `WorkflowStepHistory::eventMatchesShape` for a call kind. +fn matches_shape(event: &Event, kind: &str) -> bool { + let timer_kind = event.payload["timer_kind"].as_str(); + let timer = matches!(event.kind.as_str(), "TimerScheduled" | "TimerFired" | "TimerCancelled"); + match kind { + "activity" | "local_activity" => { + event.kind.starts_with("Activity") + && STEP_EVENTS.contains(&event.kind.as_str()) + && local_activity(event) == (kind == "local_activity") + } + "timer" => timer && !matches!(timer_kind, Some("condition_timeout" | "signal_timeout")), + "condition" => { + matches!(event.kind.as_str(), "ConditionWaitOpened" | "ConditionWaitSatisfied" | "ConditionWaitTimedOut") + || timer && timer_kind == Some("condition_timeout") + } + "signal" => matches!(event.kind.as_str(), "SignalWaitOpened" | "SignalApplied") || timer && timer_kind == Some("signal_timeout"), + "child" => event.kind.starts_with("Child") && STEP_EVENTS.contains(&event.kind.as_str()), + "service_operation" => event.kind.starts_with("ServiceCall"), + _ => false, + } +} + +/// `validateCallBoundary`: the boundary is at most the next sequence, its +/// typed history matches the call, and it did not resolve before the +/// request. +fn call_boundary(events: &[Event], next: i64, request: &Event, boundary: &Boundary) -> Option<&'static str> { + if boundary.sequence > next { + return Some("cancellation_delivery_sequence_mismatch"); + } + match boundary.kind.as_str() { + "parallel" if boundary.sequence == next => return None, + "parallel" | "selection_handle" => return Some("cancellation_scope_delivery_unavailable"), + _ => {} + } + let typed: Vec<&Event> = events + .iter() + .filter(|event| STEP_EVENTS.contains(&event.kind.as_str()) && event.payload["sequence"] == boundary.sequence) + .collect(); + if typed.iter().any(|event| !matches_shape(event, &boundary.kind)) || (boundary.sequence < next && typed.is_empty()) { + return Some("cancellation_delivery_shape_mismatch"); + } + resolved_before(events, boundary.sequence, request.sequence).then_some("cancellation_delivery_not_eligible") +} + +fn resolved_before(events: &[Event], sequence: i64, request_sequence: i64) -> bool { + events.iter().any(|event| { + event.sequence < request_sequence + && RESOLUTION_EVENTS.contains(&event.kind.as_str()) + && (event.payload["sequence"] == sequence + || event.kind == "SelectionOperationCancelled" + && event.payload["member_base_sequence"].as_i64().zip(event.payload["member_size"].as_i64()) + .is_some_and(|(base, size)| base >= 1 && base <= sequence && size > sequence - base)) + }) +} + +/// `matchesMembership`: an unscheduled boundary keeps its address; a +/// scheduled one must touch the scope's unshielded subtree. +fn matches_membership(events: &[Event], scopes: &[Scope], scope_id: &str, start: i64, span: i64) -> bool { + let mut included: BTreeSet<&str> = BTreeSet::from([scope_id]); + for scope in scopes { + if !scope.shield && included.contains(scope.parent.as_str()) { + included.insert(scope.id.as_str()); + } + } + let mut scheduled = false; + let mut affected = false; + for event in events.iter().filter(|event| OPERATION_EVENTS.contains(&event.kind.as_str())) { + let Some(sequence) = event.payload["sequence"].as_i64() else { + continue; + }; + if sequence < start || sequence - start >= span { + continue; + } + let Some(address) = membership(event) else { + return false; + }; + if address != ROOT { + let Some(member) = find(scopes, &address) else { + return false; + }; + let opened = events.iter().find(|candidate| candidate.id == member.event_id); + if member.sequence >= sequence || opened.is_none_or(|opened| opened.sequence >= event.sequence) { + return false; + } + } + scheduled = true; + affected |= included.contains(address.as_str()); + } + !scheduled || affected +} + +/// `ScopedCancellationPreparation`: a preparation, read for its own scope or +/// one of its frozen descendants. +#[derive(Clone, Debug, PartialEq)] +struct Reference { + event_id: String, + history_sequence: i64, + scope_id: String, + request_id: String, + request_event_id: String, + context: Value, + authority_deadline_at: String, + activity_members: Vec, + timer_members: Vec, +} + +fn reference(events: &[Event], scopes: &[Scope], run: &RunFacts, scope_id: &str, preparation_id: Option<&str>) -> Result> { + let event = match preparation_id { + None => match prepared(events, scopes, scope_id)? { + Some(event) => event, + None => return Ok(None), + }, + Some(preparation_id) => { + let invalid = || no("cancellation_scope_preparation_reference_invalid"); + let original = events + .iter() + .find(|event| event.id == preparation_id && event.kind == "CancellationScopeDeliveryPrepared") + .ok_or_else(invalid)?; + let prepared_scope = original.payload["scope_id"].as_str().filter(|scope| !scope.is_empty()).ok_or_else(invalid)?; + let event = prepared(events, scopes, prepared_scope)?.ok_or_else(invalid)?; + if event.id != preparation_id { + return Err(invalid()); + } + event + } + }; + let payload: &Value = &event.payload; + let snapshot = if payload["scope_id"] == scope_id { + payload.clone() + } else { + let mut matching = payload["descendant_members"].as_array().into_iter().flatten().filter(|member| member["scope_id"] == scope_id); + match (matching.next(), matching.next()) { + (Some(member), None) => member.clone(), + _ => return Err(no("cancellation_scope_descendant_not_prepared")), + } + }; + let request = accepted(events, scope_id)?.ok_or_else(|| no("cancellation_scope_preparation_reference_invalid"))?; + let context = snapshot["cancellation"].clone(); + if last_lineage(&context)["scope_id"] != scope_id + || last_lineage(&context)["workflow_run_id"] != run.id.as_str() + || context_request(&context) != snapshot["request_id"].as_str() + || request.payload["request_id"] != snapshot["request_id"] + || request.sequence >= event.sequence + { + return Err(no("cancellation_scope_preparation_reference_invalid")); + } + let list = |field: &str| snapshot[field].as_array().cloned().unwrap_or_default(); + Ok(Some(Reference { + event_id: event.id.clone(), + history_sequence: event.sequence, + scope_id: scope_id.to_owned(), + request_id: snapshot["request_id"].as_str().unwrap_or_default().to_owned(), + request_event_id: request.id.clone(), + context, + authority_deadline_at: snapshot["authority_deadline_at"].as_str().unwrap_or_default().to_owned(), + activity_members: list("activity_members"), + timer_members: list("timer_members"), + })) +} + +/// `ScopedCancellationReconciliation::earlier`: strictly earlier +/// preparations that froze the same scope. +fn earlier(events: &[Event], scopes: &[Scope], run: &RunFacts, current: &Reference) -> Result> { + let mut candidates = Vec::new(); + for event in events.iter().filter(|event| event.kind == "CancellationScopeDeliveryPrepared" && event.sequence < current.history_sequence) { + let scope = event.payload["scope_id"].as_str().ok_or_else(|| no("cancellation_scope_preparation_history_invalid"))?; + let covers = scope == current.scope_id + || event.payload["descendant_members"].as_array().into_iter().flatten().any(|member| member["scope_id"] == current.scope_id.as_str()); + if !covers { + continue; + } + if prepared(events, scopes, scope)?.is_none_or(|prepared| prepared.id != event.id) { + return Err(no("cancellation_scope_preparation_history_invalid")); + } + let original = reference(events, scopes, run, ¤t.scope_id, Some(&event.id))? + .ok_or_else(|| no("cancellation_scope_descendant_delivery_membership_mismatch"))?; + if original.request_event_id != current.request_event_id + || original.context != current.context + || original.activity_members != current.activity_members + || original.timer_members != current.timer_members + { + return Err(no("cancellation_scope_descendant_delivery_membership_mismatch")); + } + candidates.push((scope.to_owned(), original)); + } + Ok(candidates) +} + +/// The earlier candidate whose scope was delivered (before `before`, when +/// given), with its delivery marker. +fn completed<'a>(events: &'a [Event], candidates: &[(String, Reference)], before: Option) -> Option<(Reference, &'a Event)> { + for (scope, reference) in candidates { + let delivery = events + .iter() + .find(|event| event.kind == "CancellationScopeDelivered" && event.payload["scope_id"] == scope.as_str()); + if let Some(delivery) = delivery.filter(|delivery| before.is_none_or(|before| delivery.sequence < before)) { + return Some((reference.clone(), delivery)); + } + } + None +} + +fn pending(events: &[Event], scopes: &[Scope], run: &RunFacts, current: &Reference, before: Option) -> Result { + let candidates = earlier(events, scopes, run, current)?; + Ok(!candidates.is_empty() && completed(events, &candidates, before).is_none()) +} + +/// `ScopedTimerCancellation::terminal`: the timer's fire or cancellation. +fn timer_terminal<'a>(events: &'a [Event], timer_id: &str, sequence: i64) -> Result> { + let invalid = || no("cancellation_scope_timer_history_invalid"); + let mut scheduled = events.iter().filter(|event| event.kind == "TimerScheduled" && event.payload["timer_id"] == timer_id); + let (Some(scheduled), None) = (scheduled.next(), scheduled.next()) else { + return Err(invalid()); + }; + if scheduled.payload["sequence"] != sequence { + return Err(invalid()); + } + let mut terminals = events + .iter() + .filter(|event| matches!(event.kind.as_str(), "TimerCancelled" | "TimerFired") && event.payload["timer_id"] == timer_id); + let terminal = terminals.next(); + if terminals.next().is_some() { + return Err(invalid()); + } + if let Some(terminal) = terminal + && (terminal.payload["sequence"] != sequence + || terminal.sequence <= scheduled.sequence + || terminal.payload["delay_seconds"] != scheduled.payload["delay_seconds"] + || terminal.payload["fire_at"] != scheduled.payload["fire_at"]) + { + return Err(invalid()); + } + Ok(terminal) +} + +/// `assertReceipt`: a scoped timer cancellation recorded for this reference. +fn timer_receipt(events: &[Event], run: &RunFacts, reference: &Reference, terminal: &Event) -> Result<()> { + let snapshot = &terminal.payload["cancellation_scope"]; + if snapshot["schema"] != TIMER_SCOPE_SCHEMA + || snapshot["workflow_run_id"] != run.id.as_str() + || snapshot["scope_id"] != reference.scope_id.as_str() + || snapshot["request_id"] != reference.request_id.as_str() + || snapshot["request_history_event_id"] != reference.request_event_id.as_str() + || snapshot["preparation_history_event_id"] != reference.event_id.as_str() + || snapshot["authority_deadline_at"] != reference.authority_deadline_at.as_str() + || snapshot["cancellation"] != reference.context + || terminal.sequence <= reference.history_sequence + || accepted(events, &reference.scope_id)?.is_none_or(|request| request.id != reference.request_event_id) + { + return Err(no("cancellation_scope_timer_fence_not_established")); + } + Ok(()) +} + +/// The activity receipt fields of a fenced activity. +fn fenced_activity(event: &Event, context: &Value) -> Value { + let snapshot = &event.payload["cancellation_scope"]; + json!({"fenced": true, "abandoned": false, "history_event_id": event.id, "request_id": context_request(context), + "root_request_id": context["root_context"]["root_request_id"], "scope_id": last_lineage(context)["scope_id"], + "cleanup_deadline_at": last_lineage(context)["cleanup_deadline_at"], + "cancellation_scope": {"schema": snapshot["schema"], "workflow_run_id": snapshot["workflow_run_id"], + "scope_id": snapshot["scope_id"], "request_id": snapshot["request_id"], + "request_history_event_id": snapshot["request_history_event_id"], "cancellation": snapshot["cancellation"], + "authority_deadline_at": snapshot["authority_deadline_at"]}, + "waiting_for_stop": false}) +} + +/// `ScopedCancellationReconciliation::receipts`: a delivered reference's +/// receipts, rebuilt from history before its marker. +fn receipts(events: &[Event], reference: &Reference, delivery: &Event) -> Result { + let prefix: Vec = events.iter().filter(|event| event.sequence < delivery.sequence).cloned().collect(); + let mut result = Receipts::default(); + for member in &reference.timer_members { + let timer_id = member["timer_id"].as_str().unwrap_or_default(); + let terminal = timer_terminal(&prefix, timer_id, member["sequence"].as_i64().unwrap_or_default())? + .ok_or_else(|| no("cancellation_scope_timer_fence_not_established"))?; + let cancelled = terminal.kind == "TimerCancelled"; + result.timers.push(json!({"fenced": cancelled, "history_event_id": if cancelled { json!(terminal.id) } else { Value::Null }, + "timer_id": timer_id, "sequence": member["sequence"]})); + } + for member in &reference.activity_members { + let execution = &member["activity_execution_id"]; + let scheduled = prefix.iter().find(|event| event.kind == "ActivityScheduled" && &event.payload["activity_execution_id"] == execution); + let cancelled = prefix.iter().find(|event| event.kind == "ActivityCancelled" && &event.payload["activity_execution_id"] == execution); + let mut receipt = match cancelled { + Some(cancelled) => fenced_activity(cancelled, &reference.context), + None => json!({"fenced": false, + "abandoned": scheduled.is_some_and(|scheduled| scheduled.payload["activity"]["cancellation_policy"] == "abandon"), + "waiting_for_stop": false, "history_event_id": null}), + }; + receipt["sequence"] = member["sequence"].clone(); + receipt["activity_execution_id"] = execution.clone(); + result.activities.push(receipt); + } + Ok(result) +} + +#[derive(Default)] +struct Receipts { + activities: Vec, + timers: Vec, +} + +/// The refusal shapes of the open and delivery routes. +fn open_refusal(task_id: &str, reason: &str) -> Value { + json!({"opened": false, "duplicate": false, "claim_released": false, "task_id": task_id, "created_task_ids": [], "reason": reason}) +} + +fn delivery_refusal(task_id: &str, reason: &str) -> Value { + json!({"prepared": false, "delivered": false, "claim_released": false, "task_id": task_id, "created_task_ids": [], "reason": reason}) +} + +/// `WorkerProtocol::json`'s envelope. +pub(super) fn enveloped(mut response: Value) -> Value { + response["protocol_version"] = json!(super::WORKER_PROTOCOL_VERSION); + response["server_capabilities"] = capabilities(); + response +} + +/// The reasons `PortableCancellationScopeDelivery` answers itself. Any other +/// engine exception escapes it in PHP; `root_scope_requires_run_cancellation` +/// is one (a published 500), which native answers as a refusal instead. +fn delivery_reason(reason: &str) -> bool { + reason.starts_with("cancellation_scope_") + || reason.starts_with("cancellation_delivery_") + || matches!( + reason, + "invalid_cancellation_scope_delivery" | "invalid_cancellation_delivery" | "activity_cancellation_owned_by_another_request" + | "root_scope_requires_run_cancellation" + ) +} + +/// The reasons `PortableCancellationScopeOpening` answers itself. +fn open_reason(reason: &str) -> bool { + matches!( + reason, + "cancellation_scope_workflow_claim_mismatch" | "cancellation_scope_run_not_active" | "cancellation_scope_parent_not_recorded" + | "cancellation_scope_sequence_mismatch" | "cancellation_scope_history_invalid" | "cancellation_scope_replay_mismatch" + | "cancellation_scope_command_sequence_reserved" | "cancellation_scope_parent_delivery_prepared" + ) +} + + +use super::laravel_rules::{self, Rule}; + +/// Published PHP's validation rules for opening a scope, in their order. +const OPEN_RULES: &laravel_rules::Rules = &[ + ("lease_owner", &[Rule::Required, Rule::String, Rule::Max(255)]), + ("workflow_task_attempt", &[Rule::Required, Rule::Integer, Rule::Min(1)]), + ("sequence", &[Rule::Required, Rule::Integer, Rule::Min(1)]), + ("parent_scope_id", &[Rule::Sometimes, Rule::Required, Rule::String, Rule::Max(255)]), + ("shield_parent", &[Rule::Sometimes, Rule::Required, Rule::Boolean]), +]; + +/// Published PHP's validation rules for preparing or delivering a scope. +const DELIVERY_RULES: &laravel_rules::Rules = &[ + ("lease_owner", &[Rule::Required, Rule::String, Rule::Max(255)]), + ("workflow_task_attempt", &[Rule::Required, Rule::Integer, Rule::Min(1)]), + ("scope_id", &[Rule::Required, Rule::String, Rule::Max(255)]), + ("request_id", &[Rule::Required, Rule::String, Rule::Max(255)]), + ("sequence", &[Rule::Required, Rule::Integer, Rule::Min(1)]), + ("call_kind", &[Rule::Required, + Rule::In(&["activity", "local_activity", "timer", "condition", "signal", "child", "parallel", "selection_handle"])]), + ("sequence_span", &[Rule::Sometimes, Rule::Required, Rule::Integer, Rule::Min(1)]), + ("operation_sequence", &[Rule::Nullable, Rule::Integer, Rule::Min(1)]), + ("operation_sequence_span", &[Rule::Sometimes, Rule::Required, Rule::Integer, Rule::Min(1)]), +]; + +fn protocol_supported(protocol: &str) -> bool { + super::cooperative::supports(&json!(["cooperative_cancellation"]), protocol) +} + +/// A validated integer field, as PHP's `(int)` casts it. +fn integer(value: &Value) -> Option { + match value { + Value::Number(number) => number.as_i64().or_else(|| number.as_f64().map(|float| float as i64)), + Value::String(text) => text.trim().parse().ok(), + Value::Bool(flag) => Some(i64::from(*flag)), + _ => None, + } +} + +/// A validated boolean field, as PHP's `(bool)` casts it. +fn boolean(value: &Value) -> Option { + match value { + Value::Bool(flag) => Some(*flag), + Value::Number(number) => Some(number.as_i64() != Some(0)), + Value::String(text) => Some(text != "0"), + _ => None, + } +} + +/// The delivery response's preparation frame (`PortableCancellationScopeDelivery::frame`). +fn frame(response: &mut Value, run_id: &str, payload: &Value, preparation_id: &str) { + response["prepared"] = json!(true); + response["workflow_run_id"] = json!(run_id); + response["preparation_history_event_id"] = json!(preparation_id); + for field in ["scope_id", "request_id", "cancellation", "sequence", "call_kind", "sequence_span", "operation_sequence", + "operation_sequence_span", "authority_deadline_at"] + { + response[field] = payload[field].clone(); + } + for field in ["activity_members", "timer_members", "wait_members", "child_members"] { + if let Some(members) = payload.get(field) { + response[field] = members.clone(); + } + } +} + +store_methods! { + /// The run's history, as the scope engine reads it. The transaction + /// keeps what it read, so a prepare or delivery that rereads history + /// after each descendant or fence fetches only the rows written since, + /// instead of every row again while it holds the transition lock. + pub(super) async fn scope_events(tx: &mut Transaction<'_, DB>, run_id: &str) -> Result>> { + let mut events = match tx.scope_history.take() { + Some(cache) if cache.run_id == run_id => cache.events, + _ => Arc::new(Vec::new()), + }; + loop { + let after = events.last().map_or(0, |event| event.sequence); + let rows = Self::query("SELECT id,sequence,event_type,payload,workflow_command_id FROM workflow_history_events WHERE workflow_run_id=$1 AND sequence>$2 ORDER BY sequence LIMIT 10001") + .bind(run_id) + .bind(after) + .fetch_all(&mut **tx) + .await?; + #[cfg(test)] + history_cache::ROWS_READ.with(|read| read.set(read.get() + rows.len())); + if events.len() + rows.len() > 10000 { + return Err(refuse(StatusCode::SERVICE_UNAVAILABLE, "cancellation_inventory_exceeds_budget")); + } + let fetched = rows + .iter() + .map(|row| { + Ok(Event { + id: DB::string(row, "id")?, + sequence: DB::number(row, "sequence")?, + kind: DB::string(row, "event_type")?, + payload: Arc::new(DB::document_row(row, "payload")?), + command_id: DB::optional_string(row, "workflow_command_id")?, + }) + }) + .collect::>>()?; + // A cached read only grows by the rows appended after it, which + // continue its sequence without a gap. Anything else is read + // again from the start. + if !events.is_empty() && fetched.iter().zip(after + 1..).any(|(event, expected)| event.sequence != expected) { + events = Arc::new(Vec::new()); + continue; + } + if !fetched.is_empty() { + Arc::make_mut(&mut events).extend(fetched); + } + tx.scope_history = Some(HistoryCache { run_id: run_id.to_owned(), events: events.clone() }); + return Ok(events); + } + } + + pub(super) async fn scope_run(tx: &mut Transaction<'_, DB>, run_id: &str) -> Result<(DB::Row, RunFacts)> { + let run = Self::query("SELECT * FROM workflow_runs WHERE id=$1").bind(run_id).fetch_one(&mut **tx).await?; + let facts = RunFacts { + id: DB::string(&run, "id")?, + instance: DB::string(&run, "workflow_instance_id")?, + status: DB::string(&run, "status")?, + request_id: DB::optional_string(&run, "cancellation_request_command_id")?, + execution_deadline: DB::optional_instant(&run, "execution_deadline_at")?, + run_deadline: DB::optional_instant(&run, "run_deadline_at")?, + }; + Ok((run, facts)) + } + + /// The live claim, when it is still this worker's leased workflow task + /// of the run (`matchesClaim`). + async fn scope_claim(tx: &mut Transaction<'_, DB>, task_id: &str, run_id: &str, owner: &str, attempt: i64) -> Result { + let claim = Self::query("SELECT * FROM workflow_tasks WHERE id=$1").bind(task_id).fetch_optional(&mut **tx).await?; + let run = Self::query("SELECT namespace FROM workflow_runs WHERE id=$1").bind(run_id).fetch_one(&mut **tx).await?; + let Some(claim) = claim else { + return Err(no("cancellation_scope_workflow_claim_mismatch")); + }; + if DB::string(&claim, "workflow_run_id")? != run_id + || DB::string(&claim, "namespace")? != DB::string(&run, "namespace")? + || DB::string(&claim, "task_type")? != "workflow" + || DB::string(&claim, "status")? != "leased" + || DB::optional_string(&claim, "lease_owner")?.as_deref().is_none_or(|lease| lease.is_empty() || lease != owner) + || DB::number(&claim, "attempt_count")? != attempt + || attempt < 1 + || DB::optional_instant(&claim, "lease_expires_at")?.is_none_or(|expires| now() >= expires) + { + return Err(no("cancellation_scope_workflow_claim_mismatch")); + } + Ok(claim) + } + + /// The scope controller's checks before the engine, in PHP's order: the + /// task and its run in this namespace, the claim's owner, attempt and + /// issued capability, and a fresh worker registration. + async fn scope_route_gate(&self, tx: &mut Transaction<'_, DB>, task_id: &str, owner: &str, attempt: i64) + -> Result> + { + let not_found = Ok(Err((StatusCode::NOT_FOUND, "task_not_found"))); + let Some(task) = Self::query("SELECT * FROM workflow_tasks WHERE id=$1 AND namespace=$2") + .bind(task_id).bind(self.namespace.as_ref()).fetch_optional(&mut **tx).await? + else { + return not_found; + }; + let Some(run) = Self::query("SELECT * FROM workflow_runs WHERE id=$1 AND namespace=$2") + .bind(DB::string(&task, "workflow_run_id")?).bind(self.namespace.as_ref()).fetch_optional(&mut **tx).await? + else { + return not_found; + }; + if Self::scalar("SELECT COUNT(*) FROM workflow_instances WHERE id=$1 AND namespace=$2") + .bind(DB::string(&run, "workflow_instance_id")?).bind(self.namespace.as_ref()).fetch_one(&mut **tx).await? == 0 + { + return not_found; + } + if DB::optional_string(&task, "lease_owner")?.as_deref() != Some(owner) { + return Ok(Err((StatusCode::CONFLICT, "lease_owner_mismatch"))); + } + if DB::number(&task, "attempt_count")? != attempt { + return Ok(Err((StatusCode::CONFLICT, "workflow_task_attempt_mismatch"))); + } + if !Self::claim_supports_cancellation(&task)? { + return Ok(Err((StatusCode::CONFLICT, "active_claim_cancellation_not_supported"))); + } + let worker = Self::query("SELECT status,last_heartbeat_at FROM workflow_worker_registrations WHERE namespace=$1 AND worker_id=$2") + .bind(self.namespace.as_ref()).bind(owner).fetch_optional(&mut **tx).await?; + let fresh = match &worker { + Some(worker) => DB::string(worker, "status")? == "active" + && DB::optional_instant(worker, "last_heartbeat_at")? + .is_some_and(|heartbeat| heartbeat >= now() - chrono::Duration::seconds(self.worker_stale_after_seconds)), + None => false, + }; + if !fresh { + return Ok(Err((StatusCode::CONFLICT, "stale_worker_registration"))); + } + Ok(Ok((task, run))) + } + + /// POST …/cancellation-scopes/open. + pub(crate) async fn open_cancellation_scope(&self, task_id: &str, body: Value, protocol: &str) -> Result<(StatusCode, Value)> { + let short = |status: StatusCode, reason: &str| Ok((status, enveloped(json!({"opened": false, "reason": reason})))); + if !protocol_supported(protocol) { + return short(StatusCode::CONFLICT, "cancellation_scope_requires_protocol_1_20"); + } + let body = laravel_rules::normalize(&body); + if let Err(RuntimeError::Protocol { status, response }) = laravel_rules::validate(&body, OPEN_RULES) { + return Ok((status, enveloped(response))); + } + let owner = body["lease_owner"].as_str().unwrap_or_default().to_owned(); + let attempt = integer(&body["workflow_task_attempt"]).unwrap_or_default(); + let sequence = integer(&body["sequence"]).unwrap_or_default(); + let parent = body["parent_scope_id"].as_str().unwrap_or(ROOT).to_owned(); + let shield = boolean(&body["shield_parent"]).unwrap_or(false); + let mut tx = self.begin().await?; + let run = match self.scope_route_gate(&mut tx, task_id, &owner, attempt).await? { + Ok((_, run)) => run, + Err((status, reason)) => return short(status, reason), + }; + let run_id = DB::string(&run, "id")?; + match Self::open_scope(&mut tx, task_id, &run_id, &owner, attempt, sequence, &parent, shield).await { + Ok(mut response) => { + tx.commit().await?; + response["lease_owner"] = json!(owner); + response["workflow_task_attempt"] = json!(attempt); + response["history_refresh_page_token"] = json!("MA=="); + Ok((StatusCode::OK, enveloped(response))) + } + Err(RuntimeError::Refused { reason, .. }) if open_reason(reason) => { + tx.rollback().await?; + Ok((StatusCode::CONFLICT, enveloped(open_refusal(task_id, reason)))) + } + Err(error) => Err(error), + } + } + + /// `PortableCancellationScopeOpening::open` and `CancellationScopeHistory::open`. + #[allow(clippy::too_many_arguments)] + async fn open_scope(tx: &mut Transaction<'_, DB>, task_id: &str, run_id: &str, owner: &str, attempt: i64, + sequence: i64, parent: &str, shield: bool) -> Result + { + let claim = Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + let events = Self::scope_events(tx, run_id).await?; + let duplicate = events.iter().any(|event| event.kind == "CancellationScopeOpened" && event.payload["sequence"] == sequence); + let (_, facts) = Self::scope_run(tx, run_id).await?; + if facts.terminal() { + return Err(no("cancellation_scope_run_not_active")); + } + let scopes = scopes(&events, run_id)?; + let opened = |event_id: &str, scope_id: &str, parent: &str, shield: bool, sequence: i64| json!({"opened": true, + "duplicate": duplicate, "claim_released": false, "task_id": task_id, "workflow_run_id": run_id, "history_event_id": event_id, + "scope_id": scope_id, "parent_scope_id": parent, "shield_parent": shield, "sequence": sequence, "created_task_ids": [], + "reason": null}); + // A replay returns the recorded scope, even after a request. + if let Some(scope) = scopes.iter().find(|scope| scope.sequence == sequence) { + if scope.parent != parent || scope.shield != shield { + return Err(no("cancellation_scope_replay_mismatch")); + } + return Ok(opened(&scope.event_id, &scope.id, &scope.parent, scope.shield, scope.sequence)); + } + if facts.request_id.is_some() + || [facts.execution_deadline, facts.run_deadline].into_iter().flatten().any(|deadline| now() >= deadline) + { + return Err(no("cancellation_scope_run_not_active")); + } + if parent != ROOT && find(&scopes, parent).is_none() { + return Err(no("cancellation_scope_parent_not_recorded")); + } + if let Some(refusal) = admission_refusal(&events, &scopes, parent, sequence)? { + return Err(no(if refusal == "operation_cancellation_delivery_reserved" { + "cancellation_scope_command_sequence_reserved" + } else { + "cancellation_scope_parent_delivery_prepared" + })); + } + // `assertCompatible`: no other workflow step owns the sequence. + if events.iter().any(|event| { + STEP_EVENTS.contains(&event.kind.as_str()) && event.kind != "CancellationScopeOpened" && event.payload["sequence"] == sequence + }) { + return Err(no("cancellation_scope_replay_mismatch")); + } + if sequence != Self::authored_sequence(tx, run_id).await? + 1 { + return Err(no("cancellation_scope_sequence_mismatch")); + } + let scope_id = id(); + let payload = json!({"schema": SCOPE_SCHEMA, "workflow_run_id": run_id, "sequence": sequence, "scope_id": scope_id, + "parent_scope_id": parent, "shield_parent": shield, "task": Self::task_snapshot(&claim)?}); + let event_id = Self::append_returning(tx, run_id, "CancellationScopeOpened", payload, Some(task_id), None).await?; + Self::scope_progress(tx, run_id).await?; + Ok(opened(&event_id, &scope_id, parent, shield, sequence)) + } + + /// `WorkflowHistoryEvent::record` marks the run's progress. + async fn scope_progress(tx: &mut Transaction<'_, DB>, run_id: &str) -> Result<()> { + Self::query("UPDATE workflow_runs SET last_progress_at=$1 WHERE id=$2") + .bind(DB::bind_time(now())).bind(run_id).execute(&mut **tx).await?; + Ok(()) + } + + /// POST …/cancellation-scopes/prepare. + pub(crate) async fn prepare_cancellation_scope(&self, task_id: &str, body: Value, protocol: &str) -> Result<(StatusCode, Value)> { + self.scope_delivery(task_id, body, protocol, true).await + } + + /// POST …/cancellation-scopes/deliver. + pub(crate) async fn deliver_cancellation_scope(&self, task_id: &str, body: Value, protocol: &str) -> Result<(StatusCode, Value)> { + self.scope_delivery(task_id, body, protocol, false).await + } + + async fn scope_delivery(&self, task_id: &str, body: Value, protocol: &str, preparing: bool) -> Result<(StatusCode, Value)> { + let refused = |status: StatusCode, reason: &str| Ok((status, enveloped(delivery_refusal(task_id, reason)))); + if !protocol_supported(protocol) { + return refused(StatusCode::CONFLICT, "cancellation_scope_requires_protocol_1_20"); + } + let body = laravel_rules::normalize(&body); + if let Err(RuntimeError::Protocol { status, response }) = laravel_rules::validate(&body, DELIVERY_RULES) { + return Ok((status, enveloped(response))); + } + let owner = body["lease_owner"].as_str().unwrap_or_default().to_owned(); + let attempt = integer(&body["workflow_task_attempt"]).unwrap_or_default(); + let scope_id = body["scope_id"].as_str().unwrap_or_default().to_owned(); + let request_id = body["request_id"].as_str().unwrap_or_default().to_owned(); + let boundary = Boundary { + sequence: integer(&body["sequence"]).unwrap_or_default(), + kind: body["call_kind"].as_str().unwrap_or_default().to_owned(), + span: integer(&body["sequence_span"]).unwrap_or(1), + operation_sequence: integer(&body["operation_sequence"]), + operation_span: integer(&body["operation_sequence_span"]).unwrap_or(1), + }; + let mut tx = self.begin().await?; + let run = match self.scope_route_gate(&mut tx, task_id, &owner, attempt).await? { + Ok((_, run)) => run, + Err((status, reason)) => return refused(status, reason), + }; + let run_id = DB::string(&run, "id")?; + let mut response = match self.scope_delivery_engine(&mut tx, task_id, &run_id, &owner, attempt, &scope_id, &request_id, &boundary, preparing).await { + Ok(response) => { + tx.commit().await?; + response + } + Err(RuntimeError::Refused { reason, .. }) if delivery_reason(reason) => { + tx.rollback().await?; + delivery_refusal(task_id, reason) + } + Err(error) => return Err(error), + }; + self.wake.notify_waiters(); + let prepared = response["prepared"] == true; + if prepared { + response["lease_owner"] = json!(owner); + response["workflow_task_attempt"] = json!(attempt); + response["history_refresh_page_token"] = json!("MA=="); + } + let accepted = if preparing { prepared } else { response["delivered"] == true }; + let pending = !preparing && prepared && response["reason"] == "cancellation_scope_activity_stop_not_acknowledged"; + let status = if (accepted && response["reason"].is_null()) || pending { + StatusCode::OK + } else if response["reason"] == "task_not_found" { + StatusCode::NOT_FOUND + } else { + StatusCode::CONFLICT + }; + Ok((status, enveloped(response))) + } + + /// `PortableCancellationScopeDelivery::mutate`. A refusal before any + /// effect is an error, which rolls back; once the preparation replayed, + /// actor effects stay and a later refusal is answered with the receipts + /// so far. + #[allow(clippy::too_many_arguments)] + async fn scope_delivery_engine(&self, tx: &mut Transaction<'_, DB>, task_id: &str, run_id: &str, owner: &str, attempt: i64, + scope_id: &str, request_id: &str, boundary: &Boundary, preparing: bool) -> Result + { + let mut response = json!({"prepared": false, "delivered": false, "claim_released": false, "task_id": task_id, + "created_task_ids": [], "reason": null}); + Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + { + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + if !preparing && prepared(&events, &scopes, scope_id)?.is_none() { + return Err(no("cancellation_scope_delivery_not_prepared")); + } + } + let preparation = self.write_boundary(tx, task_id, run_id, owner, attempt, scope_id, request_id, boundary, true).await?; + frame(&mut response, run_id, &preparation.payload, &preparation.id); + if preparing { + response["delivered"] = json!(false); + response["history_event_id"] = json!(preparation.id); + response["reason"] = Value::Null; + return Ok(response); + } + let (_, facts) = Self::scope_run(tx, run_id).await?; + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + if let Err(error) = operation_addresses(&events, &scopes) { + return Self::scope_partial(response, error); + } + let mut references = Vec::new(); + let mut built = || -> Result<()> { + references.push(reference(&events, &scopes, &facts, scope_id, Some(&preparation.id))? + .ok_or_else(|| no("cancellation_scope_preparation_reference_invalid"))?); + for descendant in preparation.payload["descendant_members"].as_array().into_iter().flatten() { + let descendant = descendant["scope_id"].as_str().unwrap_or_default(); + references.push(reference(&events, &scopes, &facts, descendant, Some(&preparation.id))? + .ok_or_else(|| no("cancellation_scope_descendant_not_prepared"))?); + } + Ok(()) + }; + if let Err(error) = built() { + return Self::scope_partial(response, error); + } + // The whole subtree is checked before even the parent's first actor. + for reference in &references { + match pending(&events, &scopes, &facts, reference, None) { + Ok(true) => { + response["reason"] = json!("cancellation_scope_descendant_delivery_pending"); + return Ok(response); + } + Ok(false) => {} + Err(error) => return Self::scope_partial(response, error), + } + } + for field in ["activity_cancellations", "timer_cancellations", "wait_cancellations", "child_cancellations"] { + response[field] = json!([]); + } + for reference in &references { + let events = Self::scope_events(tx, run_id).await?; + let earlier = match earlier(&events, &scopes, &facts, reference) { + Ok(earlier) => earlier, + Err(error) => return Self::scope_partial(response, error), + }; + if let Some((original, delivery)) = completed(&events, &earlier, None) { + match receipts(&events, &original, delivery) { + Ok(receipts) => { + Self::scope_extend(&mut response, "timer_cancellations", receipts.timers); + Self::scope_extend(&mut response, "activity_cancellations", receipts.activities); + } + Err(error) => return Self::scope_partial(response, error), + } + continue; + } + for member in &reference.timer_members { + let timer_id = member["timer_id"].as_str().unwrap_or_default(); + match self.scoped_timer_fence(tx, task_id, run_id, owner, attempt, timer_id, reference).await { + Ok(receipt) => Self::scope_extend(&mut response, "timer_cancellations", vec![receipt]), + Err(error) => return Self::scope_partial(response, error), + } + } + for member in &reference.activity_members { + let execution = member["activity_execution_id"].as_str().unwrap_or_default(); + match self.scoped_activity_fence(tx, task_id, run_id, owner, attempt, execution, reference).await { + Ok(mut receipt) => { + receipt["sequence"] = member["sequence"].clone(); + receipt["activity_execution_id"] = member["activity_execution_id"].clone(); + Self::scope_extend(&mut response, "activity_cancellations", vec![receipt]); + } + Err(error) => return Self::scope_partial(response, error), + } + } + } + match self.write_boundary(tx, task_id, run_id, owner, attempt, scope_id, request_id, boundary, false).await { + Ok(delivery) => { + frame(&mut response, run_id, &delivery.payload, &preparation.id); + response["delivered"] = json!(true); + response["history_event_id"] = json!(delivery.id); + response["reason"] = Value::Null; + Ok(response) + } + Err(error) => Self::scope_partial(response, error), + } + } + + fn scope_extend(response: &mut Value, field: &str, receipts: Vec) { + if let Some(list) = response[field].as_array_mut() { + list.extend(receipts); + } + } + + /// A refusal after effects: the response so far, with its reason. + fn scope_partial(mut response: Value, error: RuntimeError) -> Result { + match error { + RuntimeError::Refused { reason, .. } if delivery_reason(reason) => { + response["reason"] = json!(reason); + Ok(response) + } + error => Err(error), + } + } + + /// `CancellationScopeDelivery::writeBoundary`: records a preparation or + /// a delivery marker, or returns the recorded one for the same boundary. + #[allow(clippy::too_many_arguments)] + async fn write_boundary(&self, tx: &mut Transaction<'_, DB>, task_id: &str, run_id: &str, owner: &str, attempt: i64, scope_id: &str, + request_id: &str, boundary: &Boundary, preparing: bool) -> Result + { + if let Some(invalid) = boundary_syntax(boundary) { + return Err(no(invalid)); + } + Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + let (_, facts) = Self::scope_run(tx, run_id).await?; + let mut events = Self::scope_events(tx, run_id).await?; + let mut scopes = scopes(&events, run_id)?; + let context = context(&events, &scopes, run_id, scope_id)?.filter(|context| context_request(context) == Some(request_id)) + .ok_or_else(|| no("cancellation_scope_request_mismatch"))?; + let mut deadline = authority(&facts, &events, &scopes, scope_id)?.usable()?; + let existing = if preparing { prepared(&events, &scopes, scope_id)? } else { recorded(&events, &scopes, scope_id)? }; + if let Some(existing) = existing.cloned() { + if !same_boundary(&existing.payload, boundary) { + return Err(no("cancellation_scope_delivery_mismatch")); + } + if preparing && recorded(&events, &scopes, scope_id)?.is_none() { + let request = accepted(&events, scope_id)?.ok_or_else(|| no("cancellation_scope_request_history_invalid"))?; + let next = Self::authored_sequence(tx, run_id).await? + 1; + if let Some(invalid) = call_boundary(&events, next, request, boundary) { + return Err(no(invalid)); + } + } + self.renew_hosting(tx, run_id, task_id, &existing.payload["authority_deadline_at"]).await?; + return Ok(existing); + } + if preparing && position_reserved(&events, &scopes, boundary.sequence, None)? { + return Err(no("cancellation_scope_command_sequence_reserved")); + } + let request = accepted(&events, scope_id)?.ok_or_else(|| no("cancellation_scope_request_history_invalid"))?; + if find(&scopes, scope_id).is_some_and(|scope| scope.shield) && !request.payload["parent_scope_id"].is_null() { + return Err(no("cancellation_scope_parent_shielded")); + } + let next = Self::authored_sequence(tx, run_id).await? + 1; + if let Some(invalid) = call_boundary(&events, next, request, boundary) { + return Err(no(invalid)); + } + let operation_start = boundary.operation_sequence.unwrap_or(boundary.sequence); + let operation_span = if boundary.operation_sequence.is_none() { boundary.span } else { boundary.operation_span }; + if !recorded_before(&scopes, scope_id, operation_start) || !matches_membership(&events, &scopes, scope_id, operation_start, operation_span) { + return Err(no("cancellation_scope_delivery_membership_mismatch")); + } + let preparation = if preparing { None } else { prepared(&events, &scopes, scope_id)?.cloned() }; + if !preparing { + match &preparation { + None => return Err(no("cancellation_scope_delivery_not_prepared")), + Some(preparation) if !same_boundary(&preparation.payload, boundary) => return Err(no("cancellation_scope_delivery_mismatch")), + _ => {} + } + } + let expired = |preparation: &Option| { + preparation.as_ref().is_some_and(|preparation| instant(&preparation.payload["authority_deadline_at"]).is_none_or(|at| now() >= at)) + }; + if expired(&preparation) { + return Err(no("cancellation_scope_authority_expired")); + } + if preparing { + // Requests and preparation commit together. + self.request_scope_descendants(tx, run_id, scope_id).await?; + Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + events = Self::scope_events(tx, run_id).await?; + scopes = self::scopes(&events, run_id)?; + deadline = authority(&facts, &events, &scopes, scope_id)?.usable()?; + } + let mut payload = json!({"schema": if preparing { PREPARATION_SCHEMA } else { DELIVERY_SCHEMA }, "workflow_run_id": run_id, + "scope_id": scope_id, "request_id": context_request(&context), "cancellation": context, "sequence": boundary.sequence, + "call_kind": boundary.kind, "sequence_span": boundary.span, "operation_sequence": boundary.operation_sequence, + "operation_sequence_span": boundary.operation_span, + "authority_deadline_at": preparation.as_ref().map_or_else(|| json!(deadline), |preparation| preparation.payload["authority_deadline_at"].clone())}); + if preparing { + refuse_other_members(&events, scope_id)?; + let authority_deadline = instant(&json!(deadline)).ok_or_else(|| no("cancellation_scope_authority_expired"))?; + payload["activity_members"] = json!(activity_members(&events, scope_id)?); + payload["timer_members"] = json!(timer_members(&events, scope_id)?); + payload["wait_members"] = json!([]); + payload["child_members"] = json!([]); + payload["descendant_members"] = json!(descendant_members(&events, &scopes, &facts, scope_id, authority_deadline)?); + if events.iter().any(|event| { + matches!(event.kind.as_str(), "ActivityCancelled" | "TimerCancelled" | "SignalWaitCancelled" | "ConditionWaitCancelled") + && event.payload["cancellation_scope"]["scope_id"] == scope_id + }) { + return Err(no("cancellation_scope_preparation_after_effect")); + } + } else if let Some(preparation) = &preparation { + self.assert_scope_ready(tx, &events, &scopes, &facts, &context, preparation).await?; + payload["preparation_history_event_id"] = json!(preparation.id); + } + let claim = Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + let current = authority(&facts, &events, &scopes, scope_id)?.usable()?; + if expired(&preparation) { + return Err(no("cancellation_scope_authority_expired")); + } + if current != deadline { + return Err(no("cancellation_scope_authority_changed")); + } + payload["task"] = Self::task_snapshot(&claim)?; + let kind = if preparing { "CancellationScopeDeliveryPrepared" } else { "CancellationScopeDelivered" }; + let event_id = Self::append_returning(tx, run_id, kind, payload.clone(), Some(task_id), None).await?; + Self::scope_progress(tx, run_id).await?; + self.renew_hosting(tx, run_id, task_id, &payload["authority_deadline_at"]).await?; + let sequence = Self::scope_events(tx, run_id).await?.iter().find(|event| event.id == event_id).map_or(0, |event| event.sequence); + Ok(Event { id: event_id, sequence, kind: kind.to_owned(), payload: Arc::new(payload), command_id: None }) + } + + /// `renewHosting`: raises the run's scope recovery horizon to the + /// boundary's authority and renews the claim's cleanup lease. + async fn renew_hosting(&self, tx: &mut Transaction<'_, DB>, run_id: &str, task_id: &str, authority_deadline: &Value) -> Result<()> { + let (run, _) = Self::scope_run(tx, run_id).await?; + if let Some(captured) = instant(authority_deadline) + && DB::optional_instant(&run, "cancellation_scope_recovery_until")?.is_none_or(|until| captured > until) + { + Self::query("UPDATE workflow_runs SET cancellation_scope_recovery_until=$1 WHERE id=$2") + .bind(DB::bind_time(captured)).bind(run_id).execute(&mut **tx).await?; + } + let expires = self.cancellation_lease_expiry(&run)?; + Self::query("UPDATE workflow_tasks SET lease_expires_at=$1 WHERE id=$2").bind(DB::bind_time(expires)).bind(task_id).execute(&mut **tx).await?; + tx.summary_changed(run_id); + Ok(()) + } + + /// The delivery barrier (`writeBoundary` while recording): every frozen + /// member and descendant has its fence. + async fn assert_scope_ready(&self, tx: &mut Transaction<'_, DB>, events: &[Event], scopes: &[Scope], facts: &RunFacts, context: &Value, + preparation: &Event) -> Result<()> + { + for member in preparation.payload["activity_members"].as_array().into_iter().flatten() { + Self::activity_ready(tx, events, facts, context, member["sequence"].as_i64().unwrap_or_default()).await?; + } + let own = reference(events, scopes, facts, preparation.payload["scope_id"].as_str().unwrap_or_default(), Some(&preparation.id))? + .ok_or_else(|| no("cancellation_scope_preparation_reference_invalid"))?; + Self::timers_ready(tx, events, facts, &own).await?; + for member in preparation.payload["descendant_members"].as_array().into_iter().flatten() { + let scope_id = member["scope_id"].as_str().unwrap_or_default(); + let descendant = reference(events, scopes, facts, scope_id, Some(&preparation.id))? + .ok_or_else(|| no("cancellation_scope_descendant_delivery_not_prepared"))?; + if member["request_id"] != descendant.request_id.as_str() + || member["cancellation"] != descendant.context + || member["authority_deadline_at"] != descendant.authority_deadline_at.as_str() + || member["activity_members"].as_array() != Some(&descendant.activity_members) + || member["timer_members"].as_array() != Some(&descendant.timer_members) + { + return Err(no("cancellation_scope_descendant_delivery_membership_mismatch")); + } + let candidates = earlier(events, scopes, facts, &descendant)?; + if completed(events, &candidates, None).is_some() { + continue; + } + if !candidates.is_empty() { + return Err(no("cancellation_scope_descendant_delivery_pending")); + } + for activity in &descendant.activity_members { + Self::activity_ready(tx, events, facts, &descendant.context, activity["sequence"].as_i64().unwrap_or_default()).await?; + } + Self::timers_ready(tx, events, facts, &descendant).await?; + } + Ok(()) + } + + /// `ScopedActivityDeliveryPolicy::assertReady` for one sequence. + async fn activity_ready(tx: &mut Transaction<'_, DB>, events: &[Event], facts: &RunFacts, context: &Value, sequence: i64) -> Result<()> { + let invalid = || no("cancellation_scope_activity_history_invalid"); + for scheduled in events.iter().filter(|event| event.kind == "ActivityScheduled" && event.payload["sequence"] == sequence) { + let execution = scheduled.payload["activity_execution_id"].as_str().filter(|id| !id.is_empty()).ok_or_else(invalid)?; + let duplicates = events.iter().filter(|event| { + event.kind == "ActivityScheduled" && (event.payload["activity_execution_id"] == execution || event.payload["sequence"] == sequence) + }).count(); + if scheduled.payload["activity"]["id"] != execution || duplicates != 1 { + return Err(invalid()); + } + let policy = scheduled.payload["activity"].get("cancellation_policy").cloned().unwrap_or(json!("try_cancel")); + let policy = policy.as_str().filter(|policy| matches!(*policy, "try_cancel" | "wait_cancellation_completed" | "abandon")).ok_or_else(invalid)?; + if policy == "abandon" { + return Err(no("cancellation_scope_activity_abandon_not_supported")); + } + let cancelled = events.iter().find(|event| event.kind == "ActivityCancelled" && event.payload["activity_execution_id"] == execution); + if cancelled.is_none() && activity_resolved(events, facts, execution) { + continue; + } + let Some(cancelled) = cancelled.filter(|cancelled| cancelled.payload["cancellation_scope"]["cancellation"] == *context) else { + return Err(no("cancellation_scope_activity_fence_not_established")); + }; + let row = Self::query("SELECT workflow_run_id,status,current_attempt_id FROM activity_executions WHERE id=$1") + .bind(execution).fetch_optional(&mut **tx).await?; + let established = match &row { + Some(row) => DB::string(row, "workflow_run_id")? == facts.id + && DB::string(row, "status")? == "cancelled" + && DB::optional_string(row, "current_attempt_id")?.as_deref() == cancelled.payload["activity_attempt_id"].as_str(), + None => false, + }; + if !established { + return Err(no("cancellation_scope_activity_fence_not_established")); + } + if policy == "wait_cancellation_completed" && !activity_resolved(events, facts, execution) { + return Err(no("cancellation_scope_activity_stop_not_acknowledged")); + } + } + Ok(()) + } + + /// `ScopedTimerCancellation::assertReady` for a reference's timers. + async fn timers_ready(tx: &mut Transaction<'_, DB>, events: &[Event], facts: &RunFacts, reference: &Reference) -> Result<()> { + for member in &reference.timer_members { + let timer_id = member["timer_id"].as_str().unwrap_or_default(); + let sequence = member["sequence"].as_i64().unwrap_or_default(); + let terminal = timer_terminal(events, timer_id, sequence)?.ok_or_else(|| no("cancellation_scope_timer_fence_not_established"))?; + if terminal.kind == "TimerFired" { + continue; + } + timer_receipt(events, facts, reference, terminal)?; + let timer = Self::query("SELECT workflow_run_id,sequence,status FROM workflow_run_timers WHERE id=$1") + .bind(timer_id).fetch_optional(&mut **tx).await?; + let tasks = Self::timer_tasks(tx, &facts.id, timer_id).await?; + let established = match (&timer, tasks.as_slice()) { + (Some(timer), [task]) => DB::string(timer, "workflow_run_id")? == facts.id + && DB::number(timer, "sequence")? == sequence + && DB::string(timer, "status")? == "cancelled" + && DB::string(task, "status")? == "cancelled" + && DB::optional_instant(task, "lease_expires_at")?.is_none(), + _ => false, + }; + if !established { + return Err(no("cancellation_scope_timer_fence_not_established")); + } + } + Ok(()) + } + + /// The timer tasks of a run's timer. + async fn timer_tasks(tx: &mut Transaction<'_, DB>, run_id: &str, timer_id: &str) -> Result> { + let mut tasks = Vec::new(); + for task in Self::query("SELECT * FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='timer'") + .bind(run_id).fetch_all(&mut **tx).await? + { + if DB::document_row(&task, "payload")?["timer_id"] == timer_id { + tasks.push(task); + } + } + Ok(tasks) + } + + /// `ScopedTimerCancellation::fence`. + #[allow(clippy::too_many_arguments)] + async fn scoped_timer_fence(&self, tx: &mut Transaction<'_, DB>, task_id: &str, run_id: &str, owner: &str, attempt: i64, timer_id: &str, + reference: &Reference) -> Result + { + let tasks = Self::timer_tasks(tx, run_id, timer_id).await?; + let [timer_task] = tasks.as_slice() else { + return Err(no("cancellation_scope_timer_task_mismatch")); + }; + let claim = Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + let (_, facts) = Self::scope_run(tx, run_id).await?; + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + let context = context(&events, &scopes, run_id, &reference.scope_id)? + .filter(|context| context_request(context) == Some(reference.request_id.as_str())) + .ok_or_else(|| no("cancellation_scope_request_mismatch"))?; + let current = authority(&facts, &events, &scopes, &reference.scope_id)?; + let preparation = self::reference(&events, &scopes, &facts, &reference.scope_id, Some(&reference.event_id))? + .ok_or_else(|| no("cancellation_scope_delivery_not_prepared"))?; + if pending(&events, &scopes, &facts, &preparation, None)? { + return Err(no("cancellation_scope_descendant_delivery_pending")); + } + current.usable()?; + if instant(&json!(preparation.authority_deadline_at)).is_none_or(|at| now() >= at) { + return Err(no("cancellation_scope_authority_expired")); + } + let member = preparation.timer_members.iter().find(|member| member["timer_id"] == timer_id) + .ok_or_else(|| no("cancellation_scope_timer_not_prepared"))?; + let sequence = member["sequence"].as_i64().unwrap_or_default(); + let terminal = timer_terminal(&events, timer_id, sequence)?.cloned(); + let timer = Self::query("SELECT * FROM workflow_run_timers WHERE id=$1").bind(timer_id).fetch_optional(&mut **tx).await?; + let timer = match timer { + Some(timer) if DB::string(&timer, "workflow_run_id")? == run_id && DB::number(&timer, "sequence")? == sequence => timer, + _ => return Err(no("cancellation_scope_timer_projection_mismatch")), + }; + let receipt = |fenced: bool, event: Option<&str>| json!({"fenced": fenced, "history_event_id": event, "timer_id": timer_id, "sequence": sequence}); + let event_id = match &terminal { + Some(terminal) if terminal.kind == "TimerFired" => return Ok(receipt(false, None)), + Some(terminal) => { + timer_receipt(&events, &facts, &preparation, terminal)?; + terminal.id.clone() + } + None => { + if DB::string(&timer, "status")? != "pending" || !matches!(DB::string(timer_task, "status")?.as_str(), "ready" | "leased") { + return Err(no("cancellation_scope_timer_terminal_history_not_recorded")); + } + let request = accepted(&events, &reference.scope_id)?.ok_or_else(|| no("cancellation_scope_request_history_invalid"))?; + let at = now(); + let mut payload = Self::timer_cancellation_payload(tx, run_id, &timer, at).await?; + payload["cancelled_at"] = json!(iso(at)); + payload["cancellation_scope"] = json!({"schema": TIMER_SCOPE_SCHEMA, "workflow_run_id": run_id, + "scope_id": reference.scope_id, "request_id": reference.request_id, "request_history_event_id": request.id, + "preparation_history_event_id": preparation.event_id, "cancellation": context, + "authority_deadline_at": preparation.authority_deadline_at}); + payload["task"] = Self::task_snapshot(&claim)?; + let event_id = Self::append_returning(tx, run_id, "TimerCancelled", payload, Some(task_id), None).await?; + Self::scope_progress(tx, run_id).await?; + event_id + } + }; + Self::query("UPDATE workflow_run_timers SET status='cancelled',fired_at=NULL WHERE id=$1").bind(timer_id).execute(&mut **tx).await?; + Self::query("UPDATE workflow_tasks SET status='cancelled',lease_owner=NULL,lease_expires_at=NULL WHERE id=$1") + .bind(DB::string(timer_task, "id")?).execute(&mut **tx).await?; + tx.summary_changed(run_id); + Ok(receipt(true, Some(&event_id))) + } + + /// `ScopedActivityCancellation::fence`. + #[allow(clippy::too_many_arguments)] + async fn scoped_activity_fence(&self, tx: &mut Transaction<'_, DB>, task_id: &str, run_id: &str, owner: &str, attempt: i64, + execution_id: &str, reference: &Reference) -> Result + { + let execution = Self::query("SELECT * FROM activity_executions WHERE id=$1").bind(execution_id).fetch_optional(&mut **tx).await? + .filter(|execution| DB::string(execution, "workflow_run_id").is_ok_and(|run| run == run_id)) + .ok_or_else(|| no("cancellation_scope_activity_not_found"))?; + let status = DB::string(&execution, "status")?; + let current_attempt = DB::optional_string(&execution, "current_attempt_id")?; + let attempt_row = match ¤t_attempt { + Some(attempt_id) => Self::query("SELECT * FROM activity_attempts WHERE id=$1").bind(attempt_id).fetch_optional(&mut **tx).await?, + None => None, + }; + let attempt_changed = match (¤t_attempt, &attempt_row) { + (Some(_), None) => true, + (_, Some(row)) => DB::string(row, "workflow_run_id")? != run_id || DB::string(row, "activity_execution_id")? != execution_id, + (None, None) => status == "running", + }; + if attempt_changed { + return Err(no("cancellation_scope_activity_attempt_changed")); + } + Self::scope_claim(tx, task_id, run_id, owner, attempt).await?; + let (_, facts) = Self::scope_run(tx, run_id).await?; + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + let scope_id = reference.scope_id.as_str(); + let context = context(&events, &scopes, run_id, scope_id)? + .filter(|context| context_request(context) == Some(reference.request_id.as_str())) + .ok_or_else(|| no("cancellation_scope_request_mismatch"))?; + authority(&facts, &events, &scopes, scope_id)?.usable()?; + let request = accepted(&events, scope_id)?.ok_or_else(|| no("cancellation_scope_request_history_invalid"))?; + if find(&scopes, scope_id).is_some_and(|scope| scope.shield) && !request.payload["parent_scope_id"].is_null() { + return Err(no("cancellation_scope_parent_shielded")); + } + let options = DB::optional_document_row(&execution, "activity_options")?.unwrap_or(Value::Null); + if options["cancellation_scope_id"].as_str().unwrap_or(ROOT) != scope_id + || !recorded_before(&scopes, scope_id, DB::number(&execution, "sequence")?) + { + return Err(no("cancellation_scope_activity_membership_mismatch")); + } + let preparation = self::reference(&events, &scopes, &facts, scope_id, Some(&reference.event_id))?; + if let Some(preparation) = &preparation + && pending(&events, &scopes, &facts, preparation, None)? + { + return Err(no("cancellation_scope_descendant_delivery_pending")); + } + let scheduled = events.iter().find(|event| event.kind == "ActivityScheduled" && event.payload["activity_execution_id"] == execution_id); + let policy = scheduled.and_then(|event| event.payload["activity"]["cancellation_policy"].as_str()).unwrap_or("try_cancel").to_owned(); + let unfenced = json!({"fenced": false, "abandoned": false, "waiting_for_stop": false, "history_event_id": null}); + if policy == "abandon" { + return Err(no("cancellation_scope_activity_abandon_not_supported")); + } + if matches!(status.as_str(), "completed" | "failed") { + if !activity_resolved(&events, &facts, execution_id) { + return Err(no("cancellation_scope_activity_terminal_history_not_recorded")); + } + return Ok(unfenced); + } + let preparation = preparation.ok_or_else(|| no("cancellation_scope_delivery_not_prepared"))?; + if instant(&json!(preparation.authority_deadline_at)).is_none_or(|at| now() >= at) { + return Err(no("cancellation_scope_authority_expired")); + } + let sequence = DB::number(&execution, "sequence")?; + if !preparation.activity_members.iter().any(|member| member["activity_execution_id"] == execution_id && member["sequence"] == sequence) { + return Err(no("cancellation_scope_activity_not_prepared")); + } + let metadata = json!({"schema": ACTIVITY_SCOPE_SCHEMA, "workflow_run_id": run_id, "scope_id": scope_id, + "request_id": reference.request_id, "request_history_event_id": request.id, "cancellation": context, + "authority_deadline_at": preparation.authority_deadline_at}); + let existing = events.iter().find(|event| event.kind == "ActivityCancelled" && event.payload["activity_execution_id"] == execution_id).cloned(); + let event = match existing { + Some(existing) => { + let original = existing.payload["cancellation_scope"]["request_id"].as_str() + .or_else(|| existing.payload.get("cancellation_scope").is_none().then_some(existing.command_id.as_deref()).flatten()); + if original != Some(reference.request_id.as_str()) { + return Err(no("activity_cancellation_owned_by_another_request")); + } + existing + } + None if matches!(status.as_str(), "pending" | "running") => { + self.record_scoped_activity_cancellation(tx, run_id, &execution, attempt_row.as_ref(), &reference.request_id, metadata).await? + } + None => return Err(no("cancellation_scope_activity_terminal_history_not_recorded")), + }; + let mut receipt = fenced_activity(&event, &context); + if policy == "wait_cancellation_completed" { + let events = Self::scope_events(tx, run_id).await?; + receipt["waiting_for_stop"] = json!(!activity_resolved(&events, &facts, execution_id)); + } + Ok(receipt) + } + + /// `ActivityCancellation::record` with a scope snapshot: the execution, + /// its live attempt and that attempt's task are cancelled, recorded on + /// the activity task under the scope's request. + async fn record_scoped_activity_cancellation(&self, tx: &mut Transaction<'_, DB>, run_id: &str, execution: &DB::Row, + attempt: Option<&DB::Row>, request_id: &str, metadata: Value) -> Result + { + let at = now(); + let execution_id = DB::string(execution, "id")?; + let running = DB::string(execution, "status")? == "running"; + let activity_task = if let (true, Some(attempt)) = (running, attempt) { + DB::string(attempt, "workflow_task_id")? + } else { + let mut open = Vec::new(); + for task in Self::query("SELECT id,status,payload FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='activity' AND status IN ('ready','leased')") + .bind(run_id).fetch_all(&mut **tx).await? + { + if DB::document_row(&task, "payload")?["activity_execution_id"] == execution_id.as_str() { + open.push(DB::string(&task, "id")?); + } + } + let [task] = open.as_slice() else { + return Err(no("cancellation_scope_activity_fence_not_recorded")); + }; + task.clone() + }; + Self::query("UPDATE activity_executions SET status='cancelled',closed_at=COALESCE(closed_at,$1) WHERE id=$2") + .bind(DB::bind_time(at)).bind(&execution_id).execute(&mut **tx).await?; + let mut attempt_snapshot = Value::Null; + if let Some(attempt) = attempt { + let attempt_id = DB::string(attempt, "id")?; + if !matches!(DB::string(attempt, "status")?.as_str(), "cancelled" | "completed" | "failed") { + Self::query("UPDATE activity_attempts SET status='cancelled',lease_expires_at=NULL,closed_at=COALESCE(closed_at,$1) WHERE id=$2") + .bind(DB::bind_time(at)).bind(&attempt_id).execute(&mut **tx).await?; + } + let attempt = Self::query("SELECT * FROM activity_attempts WHERE id=$1").bind(&attempt_id).fetch_one(&mut **tx).await?; + attempt_snapshot = json!({"id": attempt_id, "activity_execution_id": execution_id, "task_id": DB::string(&attempt, "workflow_task_id")?, + "attempt_number": DB::number(&attempt, "attempt_number")?, "status": DB::string(&attempt, "status")?, + "lease_owner": DB::optional_string(&attempt, "lease_owner")?, + "started_at": DB::optional_instant(&attempt, "started_at")?.map(iso), "closed_at": DB::optional_instant(&attempt, "closed_at")?.map(iso)}); + } + Self::query("UPDATE workflow_tasks SET status='cancelled',lease_expires_at=NULL,last_error=NULL WHERE id=$1") + .bind(&activity_task).execute(&mut **tx).await?; + let cancelled = Self::query("SELECT * FROM activity_executions WHERE id=$1").bind(&execution_id).fetch_one(&mut **tx).await?; + let mut payload = Self::activity_payload(&cancelled)?; + payload["workflow_command_id"] = json!(request_id); + payload["activity_attempt_id"] = attempt.map(|attempt| DB::string(attempt, "id")).transpose()?.map_or(Value::Null, Value::from); + payload["attempt_number"] = json!(DB::number(&cancelled, "attempt_count")?); + payload["cancelled_at"] = json!(DB::optional_instant(&cancelled, "closed_at")?.map_or_else(|| iso(at), iso)); + payload["activity_attempt"] = attempt_snapshot; + payload["cancellation_scope"] = metadata; + let task = Self::query("SELECT * FROM workflow_tasks WHERE id=$1").bind(&activity_task).fetch_one(&mut **tx).await?; + payload["task"] = Self::task_snapshot(&task)?; + let event_id = Self::append_returning(tx, run_id, "ActivityCancelled", payload.clone(), Some(&activity_task), Some(request_id)).await?; + Self::scope_progress(tx, run_id).await?; + tx.summary_changed(run_id); + Ok(Event { id: event_id, sequence: 0, kind: "ActivityCancelled".into(), payload: Arc::new(payload), command_id: Some(request_id.to_owned()) }) + } + + /// `CancellationScopeDescendants::request`: records a request for every + /// unshielded descendant not yet requested, with its parent's context. + pub(super) async fn request_scope_descendants(&self, tx: &mut Transaction<'_, DB>, run_id: &str, scope_id: &str) -> Result<()> { + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + for scope in descendants(&scopes, scope_id)? { + self.request_scope(tx, run_id, &scope.id, &scope.parent).await?; + } + Ok(()) + } + + /// `CancellationScopeRequests::request` with a parent: the scope's + /// accepted request, bounding live claims to the cleanup lease and + /// leaving a workflow task when the run has none open. + async fn request_scope(&self, tx: &mut Transaction<'_, DB>, run_id: &str, scope_id: &str, parent_scope_id: &str) -> Result<()> { + let open = Self::query("SELECT id,status,lease_expires_at FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='workflow' AND status IN ('ready','leased') ORDER BY id") + .bind(run_id).fetch_all(&mut **tx).await?; + let (run, facts) = Self::scope_run(tx, run_id).await?; + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + assert_scope(&scopes, scope_id)?; + if find(&scopes, scope_id).is_none_or(|scope| scope.parent != parent_scope_id) { + return Err(no("cancellation_scope_parent_mismatch")); + } + let parent = if parent_scope_id == ROOT { + run_context(&events, &facts).map(scoped_from_run) + } else { + context(&events, &scopes, run_id, parent_scope_id)? + } + .ok_or_else(|| no("cancellation_scope_parent_request_unavailable"))?; + if let Some(existing) = accepted(&events, scope_id)? { + if existing.payload["cancellation"]["root_context"]["root_request_id"] == parent["root_context"]["root_request_id"] { + return Ok(()); + } + // A run takes one request, so a competing root cannot arrive. + return Err(no("cancellation_scope_request_conflict_unavailable")); + } + let request_id = id(); + let incoming = for_descendant(&parent, &request_id, &facts, scope_id); + let current = authority(&facts, &events, &scopes, scope_id)?; + let incoming_deadline = context_deadline(&incoming); + if !current.active || incoming_deadline.is_none_or(|deadline| now() >= deadline) { + return Err(no("cancellation_scope_authority_expired")); + } + let payload = json!({"schema": REQUEST_SCHEMA, "workflow_run_id": run_id, "scope_id": scope_id, "parent_scope_id": parent_scope_id, + "request_id": request_id, "cancellation": incoming}); + let event_id = Self::append_returning(tx, run_id, "CancellationScopeRequested", payload, None, None).await?; + Self::scope_progress(tx, run_id).await?; + let ceiling = [incoming_deadline, current.deadline].into_iter().flatten().min(); + if let Some(ceiling) = ceiling + && DB::optional_instant(&run, "cancellation_scope_recovery_until")?.is_none_or(|until| ceiling > until) + { + Self::query("UPDATE workflow_runs SET cancellation_scope_recovery_until=$1 WHERE id=$2") + .bind(DB::bind_time(ceiling)).bind(run_id).execute(&mut **tx).await?; + } + let (run, _) = Self::scope_run(tx, run_id).await?; + let expiry = self.cancellation_lease_expiry(&run)?; + for task in &open { + if DB::string(task, "status")? == "leased" + && DB::optional_instant(task, "lease_expires_at")?.is_some_and(|lease| now() < lease && expiry < lease) + { + Self::query("UPDATE workflow_tasks SET lease_expires_at=$1 WHERE id=$2").bind(DB::bind_time(expiry)).bind(DB::string(task, "id")?) + .execute(&mut **tx).await?; + } + } + if open.is_empty() { + Self::create_task(tx, run_id, &DB::string(&run, "queue")?, "workflow", json!({"resume_source_kind": "cancellation_scope_request", + "resume_source_id": event_id, "workflow_command_id": request_id, "scope_id": scope_id})).await?; + } + tx.summary_changed(run_id); + Ok(()) + } + + /// `guardCancellationScopeAdmission`: a completion naming a scope needs + /// protocol 1.20, a member command type and recorded, unprepared scopes. + pub(super) async fn cancellation_scope_admission(tx: &mut Transaction<'_, DB>, task: &DB::Row, body: &Value, commands: &[Value], + protocol: Option<&str>) -> Result<()> + { + if !commands.iter().any(|command| command.get("cancellation_scope_id").is_some()) { + return Ok(()); + } + let (mut reason, unavailable) = guard_reason(commands, protocol.is_some_and(protocol_supported)); + if reason.is_none() { + let run_id = DB::string(task, "workflow_run_id")?; + let events = Self::scope_events(tx, &run_id).await?; + let scopes = scopes(&events, &run_id)?; + let next = Self::authored_sequence(tx, &run_id).await? + 1; + reason = membership_refusal(&events, &scopes, &commands.iter().collect::>(), next)?; + } + let Some(reason) = reason else { + return Ok(()); + }; + Err(RuntimeError::Protocol { + status: StatusCode::CONFLICT, + response: enveloped(json!({"task_id": DB::string(task, "id")?, "workflow_task_attempt": body["workflow_task_attempt"], + "outcome": "rejected", "recorded": false, "reason": reason, "unavailable": unavailable, "requested_version": protocol, + "minimum_protocol_version": "1.20"})), + }) + } + + /// `validateCancellationScopeMembership` inside the bridge's completion, + /// over the batch's non-terminal commands. + pub(super) async fn cancellation_scope_completion_refusal(tx: &mut Transaction<'_, DB>, run_id: &str, commands: &[&Value]) + -> Result> + { + let events = Self::scope_events(tx, run_id).await?; + let scopes = scopes(&events, run_id)?; + let next = Self::authored_sequence(tx, run_id).await? + 1; + membership_refusal(&events, &scopes, commands, next) + } +} + +/// `ActivityCancellationCompletion::resolved`: the started attempt closed +/// on its own before any fence, the fence won before any attempt started, +/// or the original owner acknowledged the stop. +fn activity_resolved(events: &[Event], facts: &RunFacts, execution_id: &str) -> bool { + let Some(scheduled) = events.iter().find(|event| event.kind == "ActivityScheduled" && event.payload["activity_execution_id"] == execution_id) else { + return false; + }; + let Some(sequence) = scheduled.payload["sequence"].as_i64().filter(|sequence| *sequence >= 1) else { + return false; + }; + let cancelled = events.iter().find(|event| event.kind == "ActivityCancelled" && event.payload["activity_execution_id"] == execution_id); + let started = events.iter().filter(|event| event.kind == "ActivityStarted" && event.payload["activity_execution_id"] == execution_id).last(); + if let Some(started) = started + && let Some(attempt) = started.payload["activity_attempt_id"].as_str() + && events.iter().any(|event| { + let closed = matches!(event.kind.as_str(), "ActivityCompleted" | "ActivityFailed") + || event.kind == "ActivityRetryScheduled" + && event.payload["activity_attempt"]["id"] == attempt + && event.payload["activity_attempt"]["activity_execution_id"] == execution_id + && event.payload["activity_attempt"]["status"] == "failed" + && event.payload["activity_attempt"]["closed_at"].is_string() + && event.payload["timeout_kind"].is_null(); + closed + && event.payload["activity_execution_id"] == execution_id + && event.payload["activity_attempt_id"] == attempt + && event.payload["sequence"] == sequence + && event.sequence > started.sequence + && cancelled.is_none_or(|cancelled| event.sequence < cancelled.sequence) + }) + { + return true; + } + let Some(cancelled) = cancelled else { + return false; + }; + if cancelled.sequence <= scheduled.sequence || cancelled.payload["sequence"] != sequence + || cancelled.payload.get("activity_attempt_id").is_none() || cancelled.payload.get("activity_attempt").is_none() + { + return false; + } + if cancelled.payload["activity_attempt_id"].is_null() { + return cancelled.payload["activity_attempt"].is_null() + && !events.iter().any(|event| event.kind == "ActivityStarted" && event.payload["activity_execution_id"] == execution_id); + } + stop_receipt(events, facts, cancelled).is_some() +} + +/// `ActivityCancellationCompletion::stopReceipt`. +fn stop_receipt<'a>(events: &'a [Event], facts: &RunFacts, cancelled: &Event) -> Option<&'a Event> { + let (request, root, deadline) = cancellation_facts(events, facts, cancelled)?; + let snapshot = &cancelled.payload["activity_attempt"]; + let attempt = cancelled.payload["activity_attempt_id"].as_str().filter(|attempt| !attempt.is_empty())?; + let execution = cancelled.payload["activity_execution_id"].as_str().filter(|execution| !execution.is_empty())?; + let owner = snapshot["lease_owner"].as_str().filter(|owner| !owner.is_empty())?; + if cancelled.command_id.as_deref() != Some(request.as_str()) || snapshot["id"] != attempt || snapshot["activity_execution_id"] != execution + || snapshot["status"] != "cancelled" + { + return None; + } + let source = if cancelled.payload["local_activity"] == true { "workflow_worker" } else { "activity_worker" }; + events.iter().find(|event| { + event.kind == "ActivityCancellationAcknowledged" + && event.sequence > cancelled.sequence + && event.command_id.as_deref() == Some(request.as_str()) + && event.payload["sequence"] == cancelled.payload["sequence"] + && event.payload["activity_execution_id"] == execution + && event.payload["activity_attempt_id"] == attempt + && event.payload["lease_owner"] == owner + && event.payload["cancellation_history_event_id"] == cancelled.id.as_str() + && event.payload["request_id"] == request.as_str() + && event.payload["root_request_id"] == root.as_str() + && event.payload["cleanup_deadline_at"] == deadline.as_str() + && event.payload["callback_state"] == "stopped" + && event.payload["evidence_source"] == source + }) +} + +/// An activity fence's request, root request and cleanup deadline: the +/// scope's accepted context for a scoped fence, else the run request's. +pub(super) fn cancellation_facts(events: &[Event], facts: &RunFacts, cancelled: &Event) -> Option<(String, String, String)> { + if let Some(snapshot) = cancelled.payload.get("cancellation_scope") { + let context = &snapshot["cancellation"]; + let request = context_request(context)?; + if snapshot["request_id"] != request { + return None; + } + let deadline = context_deadline(context).map(iso)?; + return Some((request.to_owned(), context["root_context"]["root_request_id"].as_str()?.to_owned(), deadline)); + } + let context = run_context(events, facts)?; + let request = context["request_id"].as_str()?; + if cancelled.command_id.as_deref() != Some(request) { + return None; + } + let root = context["lineage"][0]["request_id"].as_str().unwrap_or(request); + Some((request.to_owned(), root.to_owned(), iso(instant(&context["cleanup_deadline_at"])?))) +} + +/// `assertOperationAddresses`: every scheduled operation names root or a +/// recorded scope. +fn operation_addresses(events: &[Event], scopes: &[Scope]) -> Result<()> { + for event in events.iter().filter(|event| OPERATION_EVENTS.contains(&event.kind.as_str())) { + match membership(event) { + Some(address) if address == ROOT || find(scopes, &address).is_some() => {} + _ => return Err(no("cancellation_scope_delivery_history_invalid")), + } + } + Ok(()) +} + +#[cfg(test)] +mod history_cache { + use super::super::store::Store; + use std::cell::Cell; + use std::sync::Arc; + + thread_local! { + /// History rows `scope_events` fetched on this thread. + pub(super) static ROWS_READ: Cell = const { Cell::new(0) }; + } + + fn read() -> usize { + ROWS_READ.with(Cell::get) + } + + async fn append(tx: &mut super::Transaction<'_, sqlx::Sqlite>, run_id: &str, sequence: i64) { + sqlx::query("INSERT INTO workflow_history_events(id,workflow_run_id,sequence,event_type,payload) VALUES (?,?,?,?,?)") + .bind(format!("{run_id}-{sequence}")) + .bind(run_id) + .bind(sequence) + .bind("SideEffectRecorded") + .bind(format!("{{\"sequence\":{sequence}}}")) + .execute(&mut **tx) + .await + .unwrap(); + } + + #[tokio::test] + async fn a_reread_fetches_only_new_rows_and_sees_every_write() { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("history-cache.sqlite"); + let runtime = super::super::Runtime::open(path.to_str().unwrap(), "test-token".into()).await.unwrap(); + let store = runtime.sqlite_store(); + let mut tx = store.begin().await.unwrap(); + for sequence in 1..=500 { + append(&mut tx, "run-a", sequence).await; + } + append(&mut tx, "run-b", 1).await; + + let start = read(); + let first = Store::scope_events(&mut tx, "run-a").await.unwrap(); + assert_eq!((first.len(), read() - start), (500, 500)); + + // Nothing new: the same snapshot, and no row is fetched again. + let start = read(); + let again = Store::scope_events(&mut tx, "run-a").await.unwrap(); + assert!(Arc::ptr_eq(&first, &again)); + assert_eq!(read() - start, 0); + + // A write in the transaction is seen, fetching only that row. A + // snapshot already handed out keeps what it held. + append(&mut tx, "run-a", 501).await; + let start = read(); + let grown = Store::scope_events(&mut tx, "run-a").await.unwrap(); + assert_eq!((grown.len(), read() - start), (501, 1)); + assert_eq!((grown[500].sequence, &*grown[500].payload), (501, &serde_json::json!({"sequence": 501}))); + assert_eq!(first.len(), 500); + + // Another run is read in full and never mixed in. + let other = Store::scope_events(&mut tx, "run-b").await.unwrap(); + assert_eq!(other.iter().map(|event| event.id.as_str()).collect::>(), ["run-b-1"]); + let back = Store::scope_events(&mut tx, "run-a").await.unwrap(); + assert_eq!(back.len(), 501); + + // Cleared (as an in-place history update does), the next read + // fetches every row again and sees the updated payload. + sqlx::query("UPDATE workflow_history_events SET payload=? WHERE id=?") + .bind("{\"sequence\":1,\"attributed\":true}") + .bind("run-a-1") + .execute(&mut *tx) + .await + .unwrap(); + tx.scope_history = None; + let start = read(); + let fresh = Store::scope_events(&mut tx, "run-a").await.unwrap(); + assert_eq!((fresh.len(), read() - start), (501, 501)); + assert_eq!(fresh[0].payload["attributed"], true); + + // A delta that does not continue the cached sequence is never + // spliced in: the history is read again from the start. + append(&mut tx, "run-a", 505).await; + let start = read(); + let gapped = Store::scope_events(&mut tx, "run-a").await.unwrap(); + assert_eq!(read() - start, 1 + 502); + assert_eq!(gapped.iter().map(|event| event.sequence).rev().take(2).collect::>(), [505, 501]); + tx.rollback().await.unwrap(); + + // Nothing outlives the transaction: after a rollback the next one + // reads afresh and never sees the rolled-back rows. + let mut tx = store.begin().await.unwrap(); + append(&mut tx, "run-c", 1).await; + tx.commit().await.unwrap(); + let mut tx = store.begin().await.unwrap(); + let cached = Store::scope_events(&mut tx, "run-c").await.unwrap(); + append(&mut tx, "run-c", 2).await; + assert_eq!(Store::scope_events(&mut tx, "run-c").await.unwrap().len(), 2); + tx.rollback().await.unwrap(); + let mut tx = store.begin().await.unwrap(); + let start = read(); + let retried = Store::scope_events(&mut tx, "run-c").await.unwrap(); + assert_eq!((retried.len(), read() - start, cached.len()), (1, 1, 1)); + tx.rollback().await.unwrap(); + } + + async fn call(app: &axum::Router, path: &str, body: serde_json::Value) -> (axum::http::StatusCode, serde_json::Value) { + use tower::ServiceExt; + let worker = path.starts_with("/api/worker/"); + let request = axum::http::Request::builder() + .method("POST") + .uri(path) + .header("authorization", "Bearer test-token") + .header("content-type", "application/json") + .header("x-namespace", "default") + .header( + if worker { "x-durable-workflow-protocol-version" } else { "x-durable-workflow-control-plane-version" }, + if worker { "1.20" } else { "2" }, + ) + .body(axum::body::Body::from(body.to_string())) + .unwrap(); + let response = app.clone().oneshot(request).await.unwrap(); + let status = response.status(); + let bytes = axum::body::to_bytes(response.into_body(), 16 * 1024 * 1024).await.unwrap(); + (status, serde_json::from_slice(&bytes).unwrap()) + } + + /// A response without the server capability document. + fn brief(body: &serde_json::Value) -> serde_json::Value { + let mut body = body.clone(); + if let Some(object) = body.as_object_mut() { + object.remove("server_capabilities"); + } + body + } + + /// A run cancellation and a scope delivery over a wide tree read the + /// history about once, not once per descendant scope and fenced member, + /// while every member is still fenced from the history written so far. + #[tokio::test] + async fn a_wide_scope_tree_is_requested_and_delivered_reading_history_about_once() { + use axum::http::StatusCode; + use serde_json::json; + const SCOPES: i64 = 40; + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("scope-tree.sqlite"); + let runtime = super::super::Runtime::open(path.to_str().unwrap(), "test-token".into()).await.unwrap(); + let app = super::super::http::router(runtime.clone()); + let pool = runtime.sqlite_pool().clone(); + let history = || async { + sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM workflow_history_events").fetch_one(&pool).await.unwrap() as usize + }; + + let (status, body) = call(&app, "/api/worker/register", json!({"worker_id": "scope-worker", "task_queue": "scopes", + "runtime": "python", "supported_workflow_types": ["scope.tree"], "supported_activity_types": [], + "capabilities": ["cooperative_cancellation"], "capability_manifest": { + "local_activities": {"supported": false, "minimum_protocol_version": "1.18", "reason": "raw_http"}, + "worker_sessions": {"supported": false, "minimum_protocol_version": "1.18", "reason": "raw_http"}, + "sticky_execution": {"supported": false, "minimum_protocol_version": "1.18", "reason": "raw_http"}}})).await; + assert_eq!(status, StatusCode::CREATED, "{}", brief(&body)); + let (status, started) = call(&app, "/api/workflows", json!({"workflow_id": "scope-tree", "workflow_type": "scope.tree", + "task_queue": "scopes"})).await; + assert_eq!(status, StatusCode::CREATED, "{}", brief(&started)); + let run = started["run_id"].as_str().unwrap().to_owned(); + let poll = || async { + let (status, body) = call(&app, "/api/worker/workflow-tasks/poll", + json!({"worker_id": "scope-worker", "task_queue": "scopes", "timeout_seconds": 0})).await; + assert_eq!(status, StatusCode::OK, "{}", brief(&body)); + body["task"].clone() + }; + let first = poll().await; + let task = first["task_id"].as_str().unwrap().to_owned(); + let own = json!({"lease_owner": first["lease_owner"], "workflow_task_attempt": first["workflow_task_attempt"]}); + let with = |mut base: serde_json::Value, extra: serde_json::Value| { + for (key, value) in extra.as_object().unwrap() { + base[key] = value.clone(); + } + base + }; + + // One parent scope with every other scope beneath it, and a timer in + // each, after the scopes. + let mut scopes = Vec::new(); + for sequence in 1..=SCOPES { + let parent = scopes.first().cloned().unwrap_or_else(|| json!("root")); + let (status, body) = call(&app, &format!("/api/worker/workflow-tasks/{task}/cancellation-scopes/open"), + with(own.clone(), json!({"sequence": sequence, "parent_scope_id": parent}))).await; + assert_eq!((status, &body["opened"]), (StatusCode::OK, &json!(true)), "{}", brief(&body)); + scopes.push(body["scope_id"].clone()); + } + let timers: Vec<_> = scopes.iter().map(|scope| json!({"type": "start_timer", "delay_seconds": 3600, + "cancellation_scope_id": scope})).collect(); + let (status, body) = call(&app, &format!("/api/worker/workflow-tasks/{task}/complete"), + with(own.clone(), json!({"commands": timers}))).await; + assert_eq!(status, StatusCode::OK, "{}", brief(&body)); + + // The run request asks every scope. + let start = read(); + let (status, body) = call(&app, &format!("/api/workflows/scope-tree/runs/{run}/request-cancellation"), + json!({"reason": "wide tree", "cleanup_timeout_seconds": 300})).await; + assert!(status.is_success(), "{status} {}", brief(&body)); + let requested = read() - start; + let requests: Vec<(String, String)> = sqlx::query_as("SELECT json_extract(payload,'$.scope_id'),json_extract(payload,'$.request_id') FROM workflow_history_events WHERE event_type='CancellationScopeRequested' ORDER BY sequence") + .fetch_all(&pool).await.unwrap(); + assert_eq!(requests.len(), SCOPES as usize); + assert!(requested <= 2 * history().await, "the run request read {requested} history rows"); + + // The parent is prepared at its timer, freezing the others beneath it, + // and delivered: every timer is fenced, then the barrier is recorded. + let second = poll().await; + let task = second["task_id"].as_str().unwrap().to_owned(); + let boundary = json!({"lease_owner": second["lease_owner"], "workflow_task_attempt": second["workflow_task_attempt"], + "scope_id": scopes[0], "request_id": requests[0].1, "sequence": SCOPES + 1, "call_kind": "timer"}); + assert_eq!(requests[0].0, scopes[0].as_str().unwrap()); + let start = read(); + let (status, prepared) = call(&app, &format!("/api/worker/workflow-tasks/{task}/cancellation-scopes/prepare"), boundary.clone()).await; + assert_eq!((status, &prepared["prepared"]), (StatusCode::OK, &json!(true)), "{}", brief(&prepared)); + let preparing = read() - start; + let frozen: i64 = sqlx::query_scalar("SELECT json_array_length(payload,'$.descendant_members') FROM workflow_history_events WHERE event_type='CancellationScopeDeliveryPrepared'") + .fetch_one(&pool).await.unwrap(); + assert_eq!(frozen, SCOPES - 1); + assert!(preparing <= 2 * history().await, "the preparation read {preparing} history rows"); + + let start = read(); + let (status, delivered) = call(&app, &format!("/api/worker/workflow-tasks/{task}/cancellation-scopes/deliver"), boundary.clone()).await; + assert_eq!((status, &delivered["delivered"]), (StatusCode::OK, &json!(true)), "{}", brief(&delivered)); + assert_eq!(delivered["timer_cancellations"].as_array().map(Vec::len), Some(SCOPES as usize), "{}", brief(&delivered)); + let delivering = read() - start; + let after_delivery = history().await; + assert!(delivering <= 2 * after_delivery, "the delivery read {delivering} history rows"); + let cancelled: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM workflow_history_events WHERE event_type='TimerCancelled'") + .fetch_one(&pool).await.unwrap(); + assert_eq!(cancelled, SCOPES); + + // A repeated delivery answers from history: the same fences, nothing + // written. + let start = read(); + let (status, again) = call(&app, &format!("/api/worker/workflow-tasks/{task}/cancellation-scopes/deliver"), boundary).await; + assert_eq!((status, &again["delivered"], &again["history_event_id"]), (StatusCode::OK, &json!(true), &delivered["history_event_id"]), "{}", brief(&again)); + assert_eq!(again["timer_cancellations"], delivered["timer_cancellations"]); + assert_eq!(history().await, after_delivery); + assert!(read() - start <= 2 * after_delivery); + } +} diff --git a/rust/src/runtime/cooperative.rs b/rust/src/runtime/cooperative.rs index cf55e7ca5..c8d3c22ca 100644 --- a/rust/src/runtime/cooperative.rs +++ b/rust/src/runtime/cooperative.rs @@ -333,6 +333,17 @@ where .ok_or_else(|| refusal(refuse(StatusCode::CONFLICT,"cancellation_request_history_missing")))?; let request_sequence = DB::number(&requested, "sequence")?; let requested = DB::document_row(&requested, "payload")?; + // `validateRootOperationMembership`: root delivery has no frozen + // scoped inventory, so a scoped operation in its range needs scope + // delivery. + if let Some(reason) = Self::root_operation_membership(&mut tx, &run_id, sequence, span).await? { + return Err(RuntimeError::Protocol { + status: StatusCode::CONFLICT, + response: super::cancellation_scopes::enveloped(json!({"delivered":false,"task_id":task_id,"workflow_run_id":run_id, + "request_id":null,"sequence":null,"call_kind":null,"sequence_span":null,"operation_sequence":null, + "operation_sequence_span":null,"reason":reason})), + }); + } if requested["workflow_command_id"] != request_id || requested["cancellation"]["request_id"] != request_id { @@ -794,6 +805,46 @@ where }) } + async fn root_operation_membership(tx: &mut Transaction<'_, DB>, run_id: &str, start: i64, span: i64) -> Result> { + for event in Self::query("SELECT event_type,payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type IN ('ActivityScheduled','TimerScheduled','ChildWorkflowScheduled','ServiceCallStarted','ConditionWaitOpened','SignalWaitOpened') ORDER BY sequence") + .bind(run_id).fetch_all(&mut **tx).await? + { + let payload = DB::document_row(&event, "payload")?; + let Some(sequence) = payload["sequence"].as_i64() else { + continue; + }; + if sequence < start || sequence - start >= span { + continue; + } + let descriptor = match DB::string(&event, "event_type")?.as_str() { + "ActivityScheduled" => payload.get("activity"), + "TimerScheduled" => payload.get("timer"), + "ChildWorkflowScheduled" => payload.get("child_workflow"), + "ServiceCallStarted" => payload.get("service_operation"), + _ => None, + }; + if descriptor.is_some_and(|descriptor| !descriptor.is_object() && descriptor.as_array().is_none_or(|items| !items.is_empty())) { + return Ok(Some("cancellation_delivery_scope_membership_invalid")); + } + let nested = descriptor.and_then(|descriptor| descriptor.get("cancellation_scope_id")); + let scope = match (payload.get("cancellation_scope_id"), nested) { + (Some(flat), _) => flat, + (None, Some(nested)) => nested, + (None, None) => continue, + }; + let Some(scope) = scope.as_str().filter(|scope| !scope.is_empty()) else { + return Ok(Some("cancellation_delivery_scope_membership_invalid")); + }; + if nested.is_some_and(|nested| nested != scope) { + return Ok(Some("cancellation_delivery_scope_membership_invalid")); + } + if scope != super::cancellation_scopes::ROOT { + return Ok(Some("cancellation_delivery_requires_scope")); + } + } + Ok(None) + } + async fn cancel_root_timer( tx: &mut Transaction<'_, DB>, run: &DB::Row, @@ -945,11 +996,19 @@ where Ok(()) } + /// `CooperativeCancellationPolicy::resumeUndeliveredRequest`: after a + /// cooperative claim completes, an undelivered request gets a workflow + /// task when none is open. The new task is reported with the tasks the + /// completion created. pub(super) async fn resume_undelivered_cancellation( tx: &mut Transaction<'_, DB>, run_id: &str, + task: &DB::Row, queue: &str, - ) -> Result<()> { + ) -> Result> { + if !Self::claim_supports_cancellation(task)? { + return Ok(None); + } let run = Self::query("SELECT * FROM workflow_runs WHERE id=$1") .bind(run_id) .fetch_one(&mut **tx) @@ -961,17 +1020,17 @@ where || DB::optional_instant(&run, "cancellation_deadline_at")? .is_none_or(|deadline| deadline <= now()) { - return Ok(()); + return Ok(None); } let Some(request_id) = DB::optional_string(&run, "cancellation_request_command_id")? else { - return Ok(()); + return Ok(None); }; if Self::scalar("SELECT COUNT(*) FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='workflow' AND status IN ('ready','leased')") .bind(run_id).fetch_one(&mut **tx).await?==0 { - Self::create_task(tx,run_id,queue,"workflow",json!({"resume_source_kind":"cancellation_request", - "resume_source_id":request_id,"workflow_command_id":request_id})).await?; + return Self::create_task(tx,run_id,queue,"workflow",json!({"resume_source_kind":"cancellation_request", + "resume_source_id":request_id,"workflow_command_id":request_id})).await.map(Some); } - Ok(()) + Ok(None) } pub(crate) async fn request_cancellation( @@ -1119,6 +1178,9 @@ where Some(&request_id), ) .await?; + // The unshielded scope subtree is requested in the same transaction; + // the first request leaves a scope-request task when none is open. + self.request_scope_descendants(&mut tx, run_id, super::cancellation_scopes::ROOT).await?; let open_tasks = Self::scalar("SELECT COUNT(*) FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='workflow' AND status IN ('ready','leased')") .bind(run_id).fetch_one(&mut *tx).await?; if open_tasks == 0 { diff --git a/rust/src/runtime/execution.rs b/rust/src/runtime/execution.rs index e611ccf36..db8c7e212 100644 --- a/rust/src/runtime/execution.rs +++ b/rust/src/runtime/execution.rs @@ -293,6 +293,14 @@ impl Runtime { } } + #[cfg(test)] + pub(super) fn sqlite_store(&self) -> &super::store::Store { + match &*self.storage { + Storage::Sqlite(store) => store, + Storage::Postgres(_) | Storage::MySql(_) => panic!("SQLite-only store test"), + } + } + #[cfg(test)] pub(crate) fn sqlite_pool(&self) -> &sqlx::SqlitePool { match &*self.storage { @@ -1614,6 +1622,9 @@ impl Runtime { } } delegate!(acknowledge_activity_cancellation(task_id: &str, body: Value, protocol: &str) -> Result); + delegate!(open_cancellation_scope(task_id: &str, body: Value, protocol: &str) -> Result<(axum::http::StatusCode, Value)>); + delegate!(prepare_cancellation_scope(task_id: &str, body: Value, protocol: &str) -> Result<(axum::http::StatusCode, Value)>); + delegate!(deliver_cancellation_scope(task_id: &str, body: Value, protocol: &str) -> Result<(axum::http::StatusCode, Value)>); pub(crate) async fn fail_activity(&self, task_id: &str, body: Value) -> Result { let (failure, blob) = super::activity_failures::prepare_failure(&body, self.signal_codec.clone()).await?; diff --git a/rust/src/runtime/http.rs b/rust/src/runtime/http.rs index ec8d68d7c..de7d1b89c 100644 --- a/rust/src/runtime/http.rs +++ b/rust/src/runtime/http.rs @@ -262,6 +262,18 @@ pub fn router(runtime: Runtime) -> Router { "/api/worker/workflow-tasks/{task_id}/heartbeat", post(heartbeat_task), ) + .route( + "/api/worker/workflow-tasks/{task_id}/cancellation-scopes/open", + post(open_cancellation_scope), + ) + .route( + "/api/worker/workflow-tasks/{task_id}/cancellation-scopes/prepare", + post(prepare_cancellation_scope), + ) + .route( + "/api/worker/workflow-tasks/{task_id}/cancellation-scopes/deliver", + post(deliver_cancellation_scope), + ) .route( "/api/worker/workflow-tasks/{task_id}/complete", post(complete_workflow), @@ -791,6 +803,7 @@ fn worker_route(rest: &str) -> Option { | ["update-validation-tasks", "poll"] | ["update-validation-tasks", _, "approve" | "reject"] | ["workflow-tasks", _, "complete" | "fail" | "history" | "heartbeat" | "deliver-cancellation"] + | ["workflow-tasks", _, "cancellation-scopes", "open" | "prepare" | "deliver"] | ["activity-tasks", _, "complete" | "fail" | "heartbeat" | "status" | "renew-lease" | "acknowledge-cancellation"] ) .then_some(Allowed::Any) @@ -2232,6 +2245,38 @@ async fn renew_activity_lease( runtime.renew_activity_lease(&task_id, body).await.map(Json) } +/// POST …/cancellation-scopes/open, prepare and deliver: each answer is +/// the route's own body and status, in the worker envelope. +async fn open_cancellation_scope( + Extension(runtime): Extension, + Path(task_id): Path, + headers: HeaderMap, + Json(body): Json, +) -> Result<(StatusCode, Json)> { + let (status, body) = runtime.open_cancellation_scope(&task_id, body, request_protocol(&headers)?).await?; + Ok((status, Json(body))) +} + +async fn prepare_cancellation_scope( + Extension(runtime): Extension, + Path(task_id): Path, + headers: HeaderMap, + Json(body): Json, +) -> Result<(StatusCode, Json)> { + let (status, body) = runtime.prepare_cancellation_scope(&task_id, body, request_protocol(&headers)?).await?; + Ok((status, Json(body))) +} + +async fn deliver_cancellation_scope( + Extension(runtime): Extension, + Path(task_id): Path, + headers: HeaderMap, + Json(body): Json, +) -> Result<(StatusCode, Json)> { + let (status, body) = runtime.deliver_cancellation_scope(&task_id, body, request_protocol(&headers)?).await?; + Ok((status, Json(body))) +} + async fn acknowledge_activity_cancellation( Extension(runtime): Extension, Path(task_id): Path, diff --git a/rust/src/runtime/mod.rs b/rust/src/runtime/mod.rs index 2d49c97b4..92de93e11 100644 --- a/rust/src/runtime/mod.rs +++ b/rust/src/runtime/mod.rs @@ -39,6 +39,7 @@ mod backend; mod bundle_verifier; mod cancellation; mod cancellation_cascade; +mod cancellation_scopes; mod child_summary; mod child_cancellation; mod children; diff --git a/rust/src/runtime/namespace_cleanup.rs b/rust/src/runtime/namespace_cleanup.rs index 5dd4eb923..780382dc0 100644 --- a/rust/src/runtime/namespace_cleanup.rs +++ b/rust/src/runtime/namespace_cleanup.rs @@ -206,6 +206,8 @@ store_methods! { deleted.insert((*key).into(), json!(count)); } } + // The namespace's histories are gone; no cached read may survive. + tx.scope_history = None; for sql in NATIVE_CLEANUP { Self::query(sql).bind(&name).execute(&mut *tx).await?; } diff --git a/rust/src/runtime/operator_metrics.rs b/rust/src/runtime/operator_metrics.rs index 8f5ff06ff..8691494cd 100644 --- a/rust/src/runtime/operator_metrics.rs +++ b/rust/src/runtime/operator_metrics.rs @@ -1359,6 +1359,8 @@ where } deleted.insert((*key).into(), json!(DB::affected(query.execute(&mut *tx).await?))); } + // The run's history is gone; no cached read of it may survive. + tx.scope_history = None; Self::query("UPDATE workflow_runs SET details_pruned_at=$1 WHERE id=$2 AND details_pruned_at IS NULL") .bind(DB::bind_time(now())) .bind(run_id) diff --git a/rust/src/runtime/store.rs b/rust/src/runtime/store.rs index 595a00881..8af3a7def 100644 --- a/rust/src/runtime/store.rs +++ b/rust/src/runtime/store.rs @@ -1113,6 +1113,19 @@ where .unwrap_or(0); let children = Self::query("SELECT sequence FROM workflow_child_calls WHERE parent_workflow_run_id=$1 ORDER BY sequence DESC LIMIT 1") .bind(run_id).fetch_optional(&mut **tx).await?.map(|row| DB::number(&row,"sequence")).transpose()?.unwrap_or(0); + // `nextDurableCommandSequence`: a scope opening consumes its sequence, + // and a delivered scope boundary its whole span, as a delivered run + // request does. + let mut scoped = 0; + for row in Self::query("SELECT event_type,payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type IN ('CancellationScopeOpened','CancellationScopeDelivered')") + .bind(run_id).fetch_all(&mut **tx).await? + { + let payload = DB::document_row(&row, "payload")?; + if let Some(sequence) = payload["sequence"].as_i64() { + let span = payload["sequence_span"].as_i64().filter(|span| *span > 1 && DB::string(&row, "event_type").is_ok_and(|kind| kind == "CancellationScopeDelivered")); + scoped = scoped.max(span.and_then(|span| sequence.checked_add(span - 1)).unwrap_or(sequence)); + } + } let run = Self::query("SELECT cancellation_delivery_sequence,run_number FROM workflow_runs WHERE id=$1") .bind(run_id) @@ -1135,6 +1148,7 @@ where .max(timers) .max(wait_sequence) .max(children) + .max(scoped) .max(reused) .max(DB::optional_number(&run, "cancellation_delivery_sequence")?.unwrap_or(0))) } @@ -2554,6 +2568,24 @@ where // Validate the entire batch before mutation. Unknown commands and // options cannot be silently accepted as unsupported durable effects. for (index, command) in commands.iter().enumerate() { + // `cancellation_scope_id` is checked by the scope admission guard + // after ownership, before PHP's normalizer reads field scopes. + // Native records activity and timer membership only, so a scoped + // child or wait keeps its field refusal. + let unscoped; + let scoped = command.get("cancellation_scope_id").is_some(); + let command = if scoped + && !matches!(command["type"].as_str(), Some("start_child_workflow" | "open_signal_wait" | "open_condition_wait")) + { + let mut stripped = command.clone(); + if let Some(fields) = stripped.as_object_mut() { + fields.remove("cancellation_scope_id"); + } + unscoped = stripped; + &unscoped + } else { + command + }; // The command normalizer's field scopes and value rules, with // PHP's messages, before native's own refusals. A local activity // record meets the normalizer after the ownership guards, as in PHP. @@ -2561,6 +2593,14 @@ where super::completion_rules::normalizer_checks(index, command)?; } let command_type = text(command, "type")?; + // A non-member command naming a scope is refused by the admission + // guard after ownership, before PHP reads its payload. + if scoped + && matches!(command_type, "fail_workflow" | "complete_workflow" | "continue_as_new" | "record_version_marker" + | "record_side_effect" | "upsert_search_attributes" | "upsert_memo" | "complete_update" | "fail_update") + { + continue; + } match command_type { "record_local_activity" => {} "fail_workflow" => { @@ -2699,8 +2739,10 @@ where ); } Self::workflow_task_guard(&mut tx, &task, &body).await?; + // PHP admits cancellation scopes before it guards worker-session + // commands, and guards both before validating streams. + Self::cancellation_scope_admission(&mut tx, &task, &body, commands, protocol).await?; self.guard_worker_session_commands(&mut tx, &task, task_id, &body, protocol, commands).await?; - // PHP guards worker-session commands before validating streams. // `MessageStreamService::validateCompletion`, before anything changes. let streams = super::message_streams::Completion::of(&body); let stream_instance = Self::query("SELECT workflow_instance_id FROM workflow_runs WHERE id=$1") @@ -2820,6 +2862,18 @@ where "development_selection_cancellation_unqualified", )); } + // `validateCancellationScopeMembership` in the bridge's completion: a + // prepared scope admits no new member, and an undelivered preparation + // reserves its boundary for every command. + let non_terminal: Vec<&Value> = commands.iter().filter(|command| !is_terminal(command)).collect(); + if let Some(reason) = Self::cancellation_scope_completion_refusal(&mut tx, &run_id, &non_terminal).await? { + return Err(super::RuntimeError::Protocol { + status: StatusCode::CONFLICT, + response: super::cancellation_scopes::enveloped(json!({"task_id": task_id, + "workflow_task_attempt": body["workflow_task_attempt"], "outcome": "completed", "recorded": false, + "run_id": run_id, "run_status": DB::string(&run, "status")?, "created_task_ids": [], "reason": reason})), + }); + } Self::validate_update_commands(&mut tx, &run_id, &task, commands).await?; let mut sequence = Self::authored_sequence(&mut tx, &run_id).await?; // The tasks this batch's commands create, in order, as PHP reports them. @@ -2921,6 +2975,13 @@ where "schedule_to_close_timeout", scheduled_at, )?; + // PHP keeps an activity's scope in its options, after the + // worker session; its snapshot and the scope's members read it + // from there. + let mut activity_options = activity_options; + if let Some(scope) = command.get("cancellation_scope_id") { + activity_options.get_or_insert_with(|| json!({}))["cancellation_scope_id"] = scope.clone(); + } // The preserved SQL schema defaults this counter to one. // Published authored scheduling explicitly starts at zero; // only the original worker claim consumes its first attempt. @@ -2981,8 +3042,13 @@ where let scheduled_at = now(); Self::query("INSERT INTO workflow_run_timers(id,workflow_run_id,sequence,status,delay_seconds,fire_at) VALUES ($1,$2,$3,'pending',$4,$5)") .bind(&timer_id).bind(&run_id).bind(sequence).bind(delay).bind(DB::bind_time(fire_at)).execute(&mut *tx).await?; - let mut timer_payload = json!({"timer_id":timer_id,"sequence":sequence,"delay_seconds":delay,"fire_at":fire_at, - "task":Self::task_snapshot(&task)?}); + // PHP records the fire time with `toJSON` (microseconds); a + // scoped timer's descriptor reads it back exactly. + let mut timer_payload = json!({"timer_id":timer_id,"sequence":sequence,"delay_seconds":delay, + "fire_at":super::cancellation_scopes::iso(fire_at),"task":Self::task_snapshot(&task)?}); + if let Some(scope) = command.get("cancellation_scope_id") { + timer_payload["cancellation_scope_id"] = scope.clone(); + } super::selections::merge(&mut timer_payload, &metadata); Self::append_at( &mut tx, @@ -2995,6 +3061,9 @@ where ) .await?; let mut timer_task_payload = json!({"timer_id":timer_id}); + if let Some(scope) = command.get("cancellation_scope_id") { + timer_task_payload["cancellation_scope_id"] = scope.clone(); + } super::selections::merge(&mut timer_task_payload, &metadata); let timer_task = Self::create_task( &mut tx, @@ -3159,8 +3228,9 @@ where .bind(task_id) .execute(&mut *tx) .await?; - Self::resume_undelivered_cancellation(&mut tx, &run_id, &DB::string(&run, "queue")?) - .await?; + if let Some(successor) = Self::resume_undelivered_cancellation(&mut tx, &run_id, &task, &DB::string(&run, "queue")?).await? { + created.push(successor); + } // PHP reports the run's next message task with the tasks the // commands created. if let Some(next) = Self::next_message_task(&mut tx, &run_id, &DB::string(&run, "queue")?).await? { @@ -3508,7 +3578,7 @@ where let task_id = DB::string(&task, "id")?; let task_payload = DB::document_row(&task, "payload")?; let metadata = super::selections::metadata(&task_payload, "timer")?; - let mut payload = json!({"timer_id":timer_id,"sequence":DB::number(&timer,"sequence")?,"delay_seconds":DB::number(&timer,"delay_seconds")?,"fire_at":fire_at,"fired_at":fired_at}); + let mut payload = json!({"timer_id":timer_id,"sequence":DB::number(&timer,"sequence")?,"delay_seconds":DB::number(&timer,"delay_seconds")?,"fire_at":super::cancellation_scopes::iso(fire_at),"fired_at":fired_at}); if task_payload.get("signal_wait_id").is_some() { payload["timer_kind"] = json!("signal_timeout"); payload["signal_wait_id"] = task_payload["signal_wait_id"].clone(); @@ -3850,7 +3920,20 @@ where command_id: Option<&str>, recorded_at: Option>, ) -> Result<()> { - Self::record(tx, run_id, event_type, payload, task_id, command_id, recorded_at, true).await + Self::record(tx, run_id, event_type, payload, task_id, command_id, recorded_at, true).await.map(|_| ()) + } + + /// Records an event and returns its id. A command id names the command + /// without copying its snapshot, as PHP does for a bare id string. + pub(super) async fn append_returning( + tx: &mut Transaction<'_, DB>, + run_id: &str, + event_type: &str, + payload: Value, + task_id: Option<&str>, + command_id: Option<&str>, + ) -> Result { + Self::record(tx, run_id, event_type, payload, task_id, command_id, None, false).await } /// Records an event linked to a command by id only, as PHP does when it @@ -3864,7 +3947,7 @@ where task_id: Option<&str>, command_id: &str, ) -> Result<()> { - Self::record(tx, run_id, event_type, payload, task_id, Some(command_id), None, false).await + Self::record(tx, run_id, event_type, payload, task_id, Some(command_id), None, false).await.map(|_| ()) } #[allow(clippy::too_many_arguments)] @@ -3877,7 +3960,7 @@ where command_id: Option<&str>, recorded_at: Option>, command_snapshot: bool, - ) -> Result<()> { + ) -> Result { // The backend transition lock protects this read and write together. // MySQL has no UPDATE RETURNING; all adapters use this same allocation. let row = Self::query("SELECT last_history_sequence FROM workflow_runs WHERE id=$1") @@ -3917,8 +4000,9 @@ where } // A metered completion (Workflow #930) charges the event as stored. tx.charge_event(event_type, &payload)?; + let event_id = id(); Self::query("INSERT INTO workflow_history_events(id,workflow_run_id,sequence,event_type,payload,workflow_task_id,workflow_command_id,recorded_at) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)") - .bind(id()).bind(run_id).bind(sequence).bind(event_type).bind(DB::document(&payload)).bind(task_id).bind(command_id).bind(DB::bind_time(recorded_at)).execute(&mut **tx).await?; + .bind(&event_id).bind(run_id).bind(sequence).bind(event_type).bind(DB::document(&payload)).bind(task_id).bind(command_id).bind(DB::bind_time(recorded_at)).execute(&mut **tx).await?; Self::project_activity_wait_event(tx, run_id, event_type, &payload, recorded_at).await?; Self::project_timer_wait_event(tx, run_id, event_type, &payload, recorded_at).await?; Self::project_signal_wait_event(tx, run_id, event_type, &payload, recorded_at).await?; @@ -3930,7 +4014,7 @@ where Box::pin(Self::reject_received_signals(tx, run_id, reason, task_id, task)).await?; } tx.summary_changed(run_id); - Ok(()) + Ok(event_id) } /// `PendingSignalCloser::closeForTerminalEvent`: once a run closes, each @@ -3989,6 +4073,8 @@ where // The event was charged as recorded; the attribution's growth is // charged before the update (Workflow #930). tx.charge_bytes(super::history_meter::php_json_len(&payload).saturating_sub(recorded))?; + // The only in-place history change: a cached read would miss it. + tx.scope_history = None; Self::query("UPDATE workflow_history_events SET payload=$1 WHERE id=$2") .bind(DB::document(&payload)) .bind(DB::string(&event, "id")?) @@ -4062,6 +4148,15 @@ where "parallel_group_path", )?)?; super::selections::merge(&mut payload, &metadata); + // `ActivitySnapshot::fromExecution` names the options' cancellation + // policy and scope when set. + if let Some(options) = DB::optional_document_row(activity, "activity_options")? { + for field in ["cancellation_policy", "cancellation_scope_id"] { + if let Some(value) = options[field].as_str() { + payload["activity"][field] = json!(value); + } + } + } // Released history readers retain the first claim and heartbeat across // a retry even while the next attempt has not produced new progress. for field in ["started_at", "last_heartbeat_at"] { diff --git a/rust/src/runtime/transaction.rs b/rust/src/runtime/transaction.rs index c38433823..21bc4c851 100644 --- a/rust/src/runtime/transaction.rs +++ b/rust/src/runtime/transaction.rs @@ -12,6 +12,8 @@ pub(super) struct Transaction<'a, DB: Backend> { pub(super) history_budget: super::history_budget::Config, /// A workflow task completion's history meter (Workflow #930). pub(super) meter: Option, + /// The run history the cancellation scope engine has read (`scope_events`). + pub(super) scope_history: Option, } impl<'a, DB: Backend> Transaction<'a, DB> { @@ -24,6 +26,7 @@ impl<'a, DB: Backend> Transaction<'a, DB> { summaries: BTreeSet::new(), history_budget, meter: None, + scope_history: None, } } diff --git a/rust/src/runtime/wait_timeouts.rs b/rust/src/runtime/wait_timeouts.rs index 55aabfd39..56da7a245 100644 --- a/rust/src/runtime/wait_timeouts.rs +++ b/rust/src/runtime/wait_timeouts.rs @@ -104,7 +104,7 @@ where .bind(seconds).bind(DB::bind_time(fire_at)).bind(DB::optional_time((seconds == 0).then_some(fire_at))) .execute(&mut **tx).await?; let mut payload = json!({"timer_id":timer_id,"sequence":sequence,"delay_seconds":seconds, - "fire_at":fire_at,"timer_kind":if direct {"signal_timeout"} else {"condition_timeout"}}); + "fire_at":super::cancellation_scopes::iso(fire_at),"timer_kind":if direct {"signal_timeout"} else {"condition_timeout"}}); payload .as_object_mut() .unwrap() diff --git a/rust/tests/fixtures/unserved-php-routes.v2.11.1.txt b/rust/tests/fixtures/unserved-php-routes.v2.11.1.txt index 364ee1de6..2705cc909 100644 --- a/rust/tests/fixtures/unserved-php-routes.v2.11.1.txt +++ b/rust/tests/fixtures/unserved-php-routes.v2.11.1.txt @@ -21,9 +21,6 @@ POST /api/service-endpoints/{endpointName}/services/{serviceName}/operations/{op GET /api/service-endpoints/{endpointName}/services/{serviceName}/operations/{operationName}/service-calls/{serviceCallId} POST /api/service-endpoints/{endpointName}/services/{serviceName}/operations/{operationName}/service-calls/{serviceCallId}/cancel POST /api/worker/workflow-tasks/{taskId}/cancellation-scopes/checkpoint -POST /api/worker/workflow-tasks/{taskId}/cancellation-scopes/deliver -POST /api/worker/workflow-tasks/{taskId}/cancellation-scopes/open -POST /api/worker/workflow-tasks/{taskId}/cancellation-scopes/prepare POST /api/worker/workflow-tasks/{taskId}/local-activities/checkpoint POST /api/worker/workflow-tasks/{taskId}/local-activities/checkpoint-group POST /api/worker/workflow-tasks/{taskId}/local-activities/prepare diff --git a/scripts/conformance/server-parity.mjs b/scripts/conformance/server-parity.mjs index 63c3f1f94..175a27757 100644 --- a/scripts/conformance/server-parity.mjs +++ b/scripts/conformance/server-parity.mjs @@ -105,7 +105,7 @@ try { for (const flag of ['url', 'application-root']) { if (values[flag]) arguments_.push(`--${flag}`, values[flag]); } - const probe = captureProbe('php', arguments_, {directory: dirname(resolve(values.output)), timeout: fixture.memo_replay ? fixture.memo_replay.overall_budget_seconds * 1000 + 5000 : fixture.corrected_markers ? fixture.corrected_markers.overall_budget_seconds * 1000 + 5000 : fixture.namespace_signal_pair || fixture.selected_run_signal_pair ? 75000 : fixture.update_validation ? 90000 : fixture.worker_poll_fence ? fixture.worker_poll_fence.overall_budget_seconds * 1000 + 5000 : fixture.worker_sessions ? fixture.worker_sessions.overall_budget_seconds * 1000 + 5000 : fixture.worker_session_activities ? fixture.worker_session_activities.overall_budget_seconds * 1000 + 5000 : 45000, + const probe = captureProbe('php', arguments_, {directory: dirname(resolve(values.output)), timeout: fixture.memo_replay ? fixture.memo_replay.overall_budget_seconds * 1000 + 5000 : fixture.corrected_markers ? fixture.corrected_markers.overall_budget_seconds * 1000 + 5000 : fixture.namespace_signal_pair || fixture.selected_run_signal_pair || fixture.cancellation_scopes ? 75000 : fixture.update_validation ? 90000 : fixture.worker_poll_fence ? fixture.worker_poll_fence.overall_budget_seconds * 1000 + 5000 : fixture.worker_sessions ? fixture.worker_sessions.overall_budget_seconds * 1000 + 5000 : fixture.worker_session_activities ? fixture.worker_session_activities.overall_budget_seconds * 1000 + 5000 : 45000, env: {...process.env, DW_PARITY_FAILURE_EVIDENCE_DIR: dirname(resolve(values.output))}, preserveStdout: join(sourceDirectory, name)}); const item = {fixture_id: fixture.id, fixture, outcome: 'runner-blocked'}; diff --git a/scripts/conformance/server-parity/cancellation-scopes-contract.mjs b/scripts/conformance/server-parity/cancellation-scopes-contract.mjs new file mode 100644 index 000000000..1243e0ca8 --- /dev/null +++ b/scripts/conformance/server-parity/cancellation-scopes-contract.mjs @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict'; + +// Reviewed workflow-task cancellation scopes (CancellationScopeController over +// Workflow 2.10's portable scope opening and delivery), with the scope +// requests a run cancellation records and the completion admission scopes +// impose, recorded against published Server 2.11.1. The probe normalizes the +// observation: identities and digests become labels, the run requests' +// instants named anchors and other instants their format, and clock-dependent +// relations measured facts. The fixture's `expected` holds the whole +// normalized observation, with the probe's workflow prefix as `{id}`. Every +// step compares exactly. +// +// Published defect boundary: prepare and deliver with `scope_id: "root"` +// throw `root_scope_requires_run_cancellation` from Workflow's scope requests, +// which the portable delivery does not convert, so the published server +// answers Laravel's bare 500 (fixture `published_defects`). At those steps the +// published answers or the intended 409 refusal are accepted, as a set; +// nothing else is relaxed. +export function checkCancellationScopes(fixture, observation, workflowId) { + const state = observation.cancellation_scopes; + const projection = {scope: 'cancellation_scopes'}; + assert.ok(state && typeof state === 'object', 'actual cancellation scopes observation'); + if (observation.mode === 'embedded') { + assert.deepStrictEqual(state, {applicable: false, reason: 'embedded_has_no_worker_protocol'}, + 'embedded cancellation scopes explicitly inapplicable'); + return projection; + } + const definition = fixture.cancellation_scopes; + const actual = JSON.parse(JSON.stringify(state).replaceAll(workflowId, '{id}')); + for (const defect of Object.values(definition.published_defects ?? {})) { + const steps = Object.keys(defect.published); + let published = true; + try { + for (const step of steps) assert.deepStrictEqual(actual[step], defect.published[step]); + } catch { + published = false; + } + if (published) { + for (const step of steps) actual[step] = definition.expected[step]; + } + } + for (const [step, expected] of Object.entries(definition.expected)) { + assert.deepStrictEqual(actual[step], expected, `cancellation scopes ${step}`); + } + assert.deepStrictEqual(Object.keys(actual).sort(), Object.keys(definition.expected).sort(), 'cancellation scopes steps'); + return projection; +} diff --git a/scripts/conformance/server-parity/cancellation-scopes-probe.php b/scripts/conformance/server-parity/cancellation-scopes-probe.php new file mode 100644 index 000000000..813d5deef --- /dev/null +++ b/scripts/conformance/server-parity/cancellation-scopes-probe.php @@ -0,0 +1,512 @@ +}`. Relations that depend on the clock +// are recorded as measured facts, never as raw times. +function finishHttpCancellationScopes(Client $admin, array $fixture, string $workflowId, string $url): array +{ + $definition = $fixture['cancellation_scopes']; + $namespace = strtolower($workflowId.'-csc'); + $admin->createNamespace($namespace, 'cancellation scopes λ', 30); + $token = getenv('DW_PARITY_TOKEN') ?: null; + // The SQLite reference shares its database with a one-second maintenance + // loop; retry its retryable lock-pressure refusals, as the other probes do. + $stack = HandlerStack::create(); + $stack->push(LockPressurePollRetries::middleware(), 'lock_pressure_poll_retry'); + $http = new CancellationScopesHttp(['base_uri' => rtrim($url, '/'), 'http_errors' => false, + 'connect_timeout' => 1, 'timeout' => 30, 'handler' => $stack]); + $raw = static function (string $method, string $path, ?array $body = [], ?string $protocol = '1.20', ?string $space = null) use ($http, $token, $namespace): array { + $options = ['headers' => array_filter([ + 'Authorization' => $token === null ? null : 'Bearer '.$token, + 'Accept' => 'application/json', 'X-Namespace' => $space ?? $namespace, + 'X-Durable-Workflow-Control-Plane-Version' => '2', 'X-Durable-Workflow-Protocol-Version' => $protocol, + ])]; + if ($body !== null) { + $options['json'] = $body === [] ? (object) [] : $body; + } + $response = $http->request($method, $path, $options); + $decoded = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR); + // The worker envelope's capability map is recorded by presence only; + // discovery records the cooperative flag once. + if (is_array($decoded) && array_key_exists('server_capabilities', $decoded)) { + $decoded['server_capabilities'] = '{present}'; + } + unset($decoded['control_plane']); + + return ['status' => $response->getStatusCode(), 'response' => $decoded]; + }; + $ok = static function (array $recorded, int $status, string $operation): array { + if ($recorded['status'] !== $status) { + throw new RuntimeException('Cancellation scopes '.$operation.': '.json_encode($recorded)); + } + + return $recorded['response']; + }; + // Avro payload precomputed by the published serializer: []. + $empty = ['codec' => 'avro', 'blob' => $definition['empty_blob']]; + $type = $definition['workflow_type']; + $activityType = $definition['activity_type']; + $budget = $definition['cleanup_timeout_seconds']; + // Labels for identities and instants the probe knows by name. + $names = []; + $name = static function (?string $value, string $label) use (&$names): void { + if (is_string($value) && $value !== '') { + $names[strtoupper($value)] ??= '{'.$label.'}'; + } + }; + $register = static function (string $worker, string $queue, array $capabilities) use ($raw, $ok, $type, $activityType): void { + $ok($raw('POST', '/api/worker/register', ['worker_id' => $worker, 'task_queue' => $queue, 'runtime' => 'python', + 'sdk_version' => 'raw-http', 'supported_workflow_types' => [$type], 'supported_activity_types' => [$activityType], + 'capabilities' => $capabilities, 'capability_manifest' => array_fill_keys(['local_activities', 'worker_sessions', 'sticky_execution'], + ['supported' => false, 'minimum_protocol_version' => '1.18', 'reason' => 'raw_http'])]), 201, 'registration '.$worker); + }; + // Registrations go stale after 30 seconds; polls do not refresh them. + $alive = static function (string ...$workers) use ($raw, $ok): void { + foreach ($workers as $worker) { + $ok($raw('POST', '/api/worker/heartbeat', ['worker_id' => $worker]), 200, 'worker heartbeat '.$worker); + } + }; + $start = static function (string $run, string $queue) use ($raw, $ok, $type, $empty, $workflowId, $name): array { + $id = $workflowId.'-'.strtolower($run); + $started = $ok($raw('POST', '/api/workflows', ['workflow_id' => $id, 'workflow_type' => $type, 'task_queue' => $queue, + 'input' => $empty]), 201, 'start '.$run); + $name($started['run_id'], $run); + + return ['workflow_id' => $id, 'run_id' => $started['run_id'], 'label' => $run]; + }; + $poll = static function (string $worker, string $queue, string $label, string $protocol = '1.20') use ($raw, $ok, $name): array { + $before = microtime(true); + $task = $ok($raw('POST', '/api/worker/workflow-tasks/poll', ['worker_id' => $worker, 'task_queue' => $queue, + 'timeout_seconds' => 0], $protocol), 200, 'poll '.$label)['task'] ?? null; + if (! is_array($task)) { + throw new RuntimeException('Cancellation scopes poll '.$label.': no task'); + } + $name($task['task_id'], $label); + + return $task + ['_polled_at' => [$before, microtime(true)]]; + }; + $own = static fn (array $task): array => ['lease_owner' => $task['lease_owner'], 'workflow_task_attempt' => $task['workflow_task_attempt']]; + $route = static fn (string $action, array $task, ?array $body, ?string $protocol = '1.20', ?string $space = null, ?string $taskId = null): array => $raw('POST', + '/api/worker/workflow-tasks/'.($taskId ?? $task['task_id']).'/cancellation-scopes/'.$action, $body, $protocol, $space); + $complete = static fn (array $task, array $commands, ?string $protocol = '1.20'): array => $raw('POST', + '/api/worker/workflow-tasks/'.$task['task_id'].'/complete', $own($task) + ['commands' => $commands], $protocol); + $timer = static fn (?string $scope): array => ['type' => 'start_timer', 'delay_seconds' => 3600] + + ($scope === null ? [] : ['cancellation_scope_id' => $scope]); + $events = static fn (array $run): array => $ok($raw('GET', '/api/workflows/'.$run['workflow_id'].'/runs/'.$run['run_id'].'/history', null), + 200, 'history '.$run['label'])['events'] ?? []; + // Whether an instant lies `seconds` after a measured window, within a + // half-second of either end. + $after = static function (?string $instant, array $window, float $seconds): bool { + if (! is_string($instant)) { + return false; + } + $at = (float) (new DateTimeImmutable($instant))->format('U.u'); + + return $at >= $window[0] + $seconds - 0.5 && $at <= $window[1] + $seconds + 0.5; + }; + // Whether an instant lies after another by at most `seconds` (and a + // half-second of rounding). + $within = static fn (?string $later, ?string $earlier, float $seconds): bool => is_string($later) && is_string($earlier) + && ($gap = (float) (new DateTimeImmutable($later))->format('U.u') - (float) (new DateTimeImmutable($earlier))->format('U.u')) > 0 + && $gap <= $seconds + 0.5; + $seconds = static fn (?string $later, ?string $earlier): ?int => is_string($later) && is_string($earlier) + ? (int) round((float) (new DateTimeImmutable($later))->format('U.u') - (float) (new DateTimeImmutable($earlier))->format('U.u')) + : null; + // A history event's claim or task snapshot, without its timing fields. + $snapshot = static fn (mixed $task): mixed => is_array($task) ? array_intersect_key($task, + array_flip(['id', 'type', 'status', 'lease_owner', 'attempt_count'])) : $task; + $field = static fn (array $values, string $key): mixed => array_key_exists($key, $values) ? $values[$key] : '{absent}'; + // A history projection: every event's type, the scope events whole (claim + // snapshots projected), the scope of each scheduled member, and the + // scope-relevant part of a cancelled activity. Other payloads are covered + // by their own fixtures. The worker view also carries each event's task + // and command columns. + $project = static function (array $raw, bool $columns) use ($snapshot, $field, $within): array { + $projected = []; + foreach ($raw as $event) { + $payload = $event['payload'] ?? []; + $item = ['sequence' => $event['sequence'] ?? null, 'event_type' => $event['event_type'] ?? null]; + if ($columns) { + $item += ['workflow_task_id' => $event['workflow_task_id'] ?? null, 'workflow_command_id' => $event['workflow_command_id'] ?? null]; + } + switch ($item['event_type']) { + case 'CancellationScopeOpened': + case 'CancellationScopeRequested': + case 'CancellationScopeDeliveryPrepared': + case 'CancellationScopeDelivered': + case 'TimerCancelled': + case 'ActivityCancellationAcknowledged': + if (array_key_exists('task', $payload)) { + if (in_array($item['event_type'], ['CancellationScopeDeliveryPrepared', 'CancellationScopeDelivered'], true)) { + // Preparation and delivery hold the claim on the + // ten-second cleanup lease, renewed before they record. + $item['claim_lease_within_ten_seconds'] = $within($payload['task']['lease_expires_at'] ?? null, + $event['recorded_at'] ?? $event['timestamp'] ?? null, 10.0); + } + $payload['task'] = $snapshot($payload['task']); + } + $item['payload'] = $payload; + break; + case 'ActivityScheduled': + $item['payload'] = ['sequence' => $payload['sequence'] ?? null, + 'cancellation_scope_id' => $field($payload, 'cancellation_scope_id'), + 'activity.cancellation_scope_id' => $field($payload['activity'] ?? [], 'cancellation_scope_id'), + 'activity.cancellation_policy' => $field($payload['activity'] ?? [], 'cancellation_policy')]; + break; + case 'TimerScheduled': + $item['payload'] = ['sequence' => $payload['sequence'] ?? null, + 'cancellation_scope_id' => $field($payload, 'cancellation_scope_id')]; + break; + case 'ActivityCancelled': + $activity = $payload['activity'] ?? []; + $attempt = $payload['activity_attempt'] ?? null; + $item['payload'] = array_diff_key($payload, array_flip(['activity', 'activity_attempt', 'task'])) + [ + 'activity' => ['id' => $field($activity, 'id'), 'sequence' => $field($activity, 'sequence'), + 'status' => $field($activity, 'status'), 'attempt_id' => $field($activity, 'attempt_id'), + 'cancellation_scope_id' => $field($activity, 'cancellation_scope_id'), + 'cancellation_policy' => $field($activity, 'cancellation_policy')], + 'activity_attempt' => is_array($attempt) ? array_intersect_key($attempt, + array_flip(['id', 'activity_execution_id', 'task_id', 'status', 'lease_owner'])) : $attempt, + 'task' => $snapshot($payload['task'] ?? null)]; + break; + } + $projected[] = $item; + } + + return $projected; + }; + $history = static fn (array $run): array => $project($events($run), false); + // The worker's view of the claimed run's history, page by page from the + // refresh token scope routes return. + $taskHistory = static function (array $task) use ($raw, $ok, $own, $project): array { + $all = []; + $token = 'MA=='; + do { + $page = $ok($raw('POST', '/api/worker/workflow-tasks/'.$task['task_id'].'/history', + $own($task) + ['next_history_page_token' => $token]), 200, 'task history'); + array_push($all, ...($page['history_events'] ?? [])); + $token = $page['next_history_page_token'] ?? null; + } while ($token !== null); + + return $project($all, true); + }; + // The scope request ids history records for a run, by scope. + $requests = static function (array $run) use ($events): array { + $found = []; + foreach ($events($run) as $event) { + if (($event['event_type'] ?? null) === 'CancellationScopeRequested') { + $found[$event['payload']['scope_id']] = $event['payload']['request_id']; + } + } + + return $found; + }; + $cancel = static fn (array $run): array => $raw('POST', '/api/workflows/'.$run['workflow_id'].'/runs/'.$run['run_id'].'/request-cancellation', + ['reason' => 'cancellation scopes fixture λ', 'cleanup_timeout_seconds' => $budget]); + // The claim a cancellation request leaves for the workflow worker. + $claimed = static function (array $task) use ($after): array { + return ['resume_source_kind' => $task['resume_source_kind'] ?? '{absent}', 'resume_source_id' => $task['resume_source_id'] ?? '{absent}', + 'workflow_wait_kind' => $task['workflow_wait_kind'] ?? '{absent}', 'open_wait_id' => $task['open_wait_id'] ?? '{absent}', + 'workflow_task_attempt' => $task['workflow_task_attempt'] ?? null, 'cancellation_request' => $task['cancellation_request'] ?? null, + 'lease_ten_seconds_from_poll' => $after($task['lease_expires_at'] ?? null, $task['_polled_at'], 10.0)]; + }; + $boundary = static fn (array $task, string $scope, string $request, int $sequence, string $kind, array $extra = []): array => $own($task) + + ['scope_id' => $scope, 'request_id' => $request, 'sequence' => $sequence, 'call_kind' => $kind] + $extra; + $heartbeat = static fn (array $task): int => $raw('POST', '/api/worker/workflow-tasks/'.$task['task_id'].'/heartbeat', $own($task))['status']; + + $state = ['applicable' => true, 'namespace' => $namespace]; + $cluster = $ok($raw('GET', '/api/cluster/info', null), 200, 'cluster info'); + $capabilities = $cluster['worker_protocol']['server_capabilities'] ?? []; + $state['discovery'] = ['cooperative_cancellation' => $capabilities['cooperative_cancellation'] ?? 'absent', + 'scope_capabilities' => array_values(array_filter(array_keys($capabilities), static fn ($key): bool => str_contains((string) $key, 'scope')))]; + + // ---- R1: the scope tree, its members, a request and S1's delivery ---- + $queue = $workflowId.'-r1-queue'; + $worker = $workflowId.'-r1-worker'; + $activityQueue = $workflowId.'-r1-activities'; + $activityWorker = $workflowId.'-r1-activity-worker'; + $register($worker, $queue, ['cooperative_cancellation']); + $register($activityWorker, $activityQueue, ['cooperative_cancellation']); + $r1 = $start('R1', $queue); + $t1 = $poll($worker, $queue, 'R1.T1'); + $owned = $own($t1); + + // Gates and validation, in the controller's order. + $state['open_protocol_missing'] = $route('open', $t1, $owned + ['sequence' => 1], null); + $state['open_protocol_1_19'] = $route('open', $t1, [], '1.19'); + $state['open_protocol_1_21'] = $route('open', $t1, $owned + ['sequence' => 1], '1.21'); + $state['open_missing_fields'] = $route('open', $t1, []); + $state['open_invalid_fields'] = $route('open', $t1, ['lease_owner' => 5, 'workflow_task_attempt' => 0, 'sequence' => 'x', + 'parent_scope_id' => '', 'shield_parent' => 'yes']); + $state['open_null_parent'] = $route('open', $t1, $owned + ['sequence' => 1, 'parent_scope_id' => null, 'shield_parent' => null]); + $state['open_unknown_task'] = $route('open', $t1, $owned + ['sequence' => 1], taskId: 'no-such-task'); + $state['open_unknown_namespace'] = $route('open', $t1, $owned + ['sequence' => 1], space: $namespace.'-missing'); + $state['open_foreign_owner'] = $route('open', $t1, ['lease_owner' => 'someone-else', 'workflow_task_attempt' => 1, 'sequence' => 1]); + $state['open_wrong_attempt'] = $route('open', $t1, ['lease_owner' => $t1['lease_owner'], 'workflow_task_attempt' => 2, 'sequence' => 1]); + $state['open_sequence_ahead'] = $route('open', $t1, $owned + ['sequence' => 2]); + $state['open_unknown_parent'] = $route('open', $t1, $owned + ['sequence' => 1, 'parent_scope_id' => 'NO-SUCH-SCOPE']); + + // The tree. Digit strings pass Laravel's integer rule, and the parent is + // trimmed. Replays return the recorded scope; another shield does not. + $state['open_s1'] = $route('open', $t1, ['lease_owner' => $t1['lease_owner'], 'workflow_task_attempt' => '1', 'sequence' => '1', + 'parent_scope_id' => ' root ']); + $s1 = $state['open_s1']['response']['scope_id'] ?? 'missing-s1'; + $name($s1, 'R1.S1'); + $state['open_s1_again'] = $route('open', $t1, $owned + ['sequence' => 1]); + $state['open_s1_explicit_root'] = $route('open', $t1, $owned + ['sequence' => 1, 'parent_scope_id' => 'root', 'shield_parent' => false]); + $state['open_s1_shielded_replay'] = $route('open', $t1, $owned + ['sequence' => 1, 'shield_parent' => true]); + $state['open_s2'] = $route('open', $t1, $owned + ['sequence' => 2, 'parent_scope_id' => $s1]); + $s2 = $state['open_s2']['response']['scope_id'] ?? 'missing-s2'; + $name($s2, 'R1.S2'); + $state['open_s3'] = $route('open', $t1, $owned + ['sequence' => 3, 'parent_scope_id' => $s1, 'shield_parent' => true]); + $s3 = $state['open_s3']['response']['scope_id'] ?? 'missing-s3'; + $name($s3, 'R1.S3'); + $state['open_s4'] = $route('open', $t1, $owned + ['sequence' => 4, 'parent_scope_id' => $s2]); + $name($state['open_s4']['response']['scope_id'] ?? null, 'R1.S4'); + + // Completion admission by scope, then the members: S1's activity at 5, + // S2's timer at 6 and shielded S3's timer at 7. + $state['complete_scoped_side_effect'] = $complete($t1, [['type' => 'record_side_effect', 'result' => null, 'cancellation_scope_id' => $s1]]); + $state['complete_unknown_scope'] = $complete($t1, [$timer('NO-SUCH-SCOPE')]); + $state['complete_scope_protocol_1_19'] = $complete($t1, [$timer($s2)], '1.19'); + $state['complete_members'] = $complete($t1, [ + ['type' => 'schedule_activity', 'activity_type' => $activityType, 'arguments' => $empty, 'queue' => $activityQueue, + 'start_to_close_timeout' => 600, 'cancellation_scope_id' => $s1], + $timer($s2), $timer($s3)]); + $activity = $ok($raw('POST', '/api/worker/activity-tasks/poll', ['worker_id' => $activityWorker, 'task_queue' => $activityQueue, + 'timeout_seconds' => 0]), 200, 'activity claim')['task'] ?? []; + $name($activity['task_id'] ?? null, 'R1.activity.task'); + $name($activity['activity_attempt_id'] ?? null, 'R1.activity.attempt'); + $name($activity['activity_execution_id'] ?? null, 'R1.activity'); + $attempt = ['activity_attempt_id' => $activity['activity_attempt_id'] ?? null, 'lease_owner' => $activity['lease_owner'] ?? null]; + $state['history_after_members'] = $history($r1); + + // The run request asks every unshielded scope, S3 excepted, and leaves a + // scope-request task. + $alive($worker, $activityWorker); + $state['request'] = $cancel($r1); + $runRequest = $state['request']['response']['cancellation_request'] ?? []; + $name($runRequest['request_id'] ?? null, 'R1.request'); + $name($runRequest['requested_at'] ?? null, 'R1.requested_at'); + $name($runRequest['cleanup_deadline_at'] ?? null, 'R1.cleanup_deadline_at'); + $state['request_budget_seconds'] = $seconds($runRequest['cleanup_deadline_at'] ?? null, $runRequest['requested_at'] ?? null); + $scopeRequests = $requests($r1); + $name($scopeRequests[$s1] ?? null, 'R1.S1.request'); + $name($scopeRequests[$s2] ?? null, 'R1.S2.request'); + $name($scopeRequests[$state['open_s4']['response']['scope_id'] ?? ''] ?? null, 'R1.S4.request'); + $state['history_after_request'] = $history($r1); + $t2 = $poll($worker, $queue, 'R1.T2'); + $state['claim_after_request'] = $claimed($t2); + $rq1 = $scopeRequests[$s1] ?? 'missing-request'; + $rq2 = $scopeRequests[$s2] ?? 'missing-request'; + + // Refusals on the new claim. The root delivery cannot reach a scoped + // member; scope routes refuse the protocol, validation, ownership, + // request and boundary faults. + $state['root_delivery_at_scoped_activity'] = $raw('POST', '/api/worker/workflow-tasks/'.$t2['task_id'].'/deliver-cancellation', + $own($t2) + ['request_id' => $runRequest['request_id'] ?? 'missing', 'sequence' => 5, 'call_kind' => 'activity']); + $state['prepare_protocol_1_19'] = $route('prepare', $t2, [], '1.19'); + $state['deliver_protocol_1_19'] = $route('deliver', $t2, [], '1.19'); + $state['prepare_missing_fields'] = $route('prepare', $t2, []); + $state['prepare_invalid_fields'] = $route('prepare', $t2, ['lease_owner' => 1, 'workflow_task_attempt' => 0, 'scope_id' => [], + 'request_id' => 2, 'sequence' => 0, 'call_kind' => 'service_operation', 'sequence_span' => null, 'operation_sequence' => 0, + 'operation_sequence_span' => 0]); + $state['prepare_unknown_task'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 5, 'activity'), taskId: 'no-such-task'); + $state['prepare_completed_claim'] = $route('prepare', $t1, $boundary($t1, $s1, $rq1, 5, 'activity')); + $state['prepare_foreign_owner'] = $route('prepare', $t2, ['lease_owner' => 'someone-else'] + $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['prepare_wrong_request'] = $route('prepare', $t2, $boundary($t2, $s1, 'NO-SUCH-REQUEST', 5, 'activity')); + $state['prepare_unknown_scope'] = $route('prepare', $t2, $boundary($t2, 'NO-SUCH-SCOPE', $rq1, 5, 'activity')); + $state['prepare_shielded_scope'] = $route('prepare', $t2, $boundary($t2, $s3, $rq1, 7, 'timer')); + $state['prepare_root_scope'] = $route('prepare', $t2, $boundary($t2, 'root', $rq1, 5, 'activity')); + $state['deliver_root_scope'] = $route('deliver', $t2, $boundary($t2, 'root', $rq1, 5, 'activity')); + $state['deliver_before_prepare'] = $route('deliver', $t2, $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['prepare_sequence_ahead'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 99, 'activity')); + $state['prepare_shape_mismatch'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 5, 'timer')); + $state['prepare_activity_span'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 5, 'activity', ['sequence_span' => 2])); + $state['open_after_request'] = $route('open', $t2, $own($t2) + ['sequence' => 8]); + $state['open_replay_after_request'] = $route('open', $t2, $own($t2) + ['sequence' => 1]); + $state['claim_heartbeat'] = $heartbeat($t2); + + // S1 prepared at its activity: its own activity, and S2 and S4 frozen as + // descendants. A replay returns the same preparation; another boundary + // does not. + $state['prepare_s1'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['prepare_s1_again'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['prepare_s1_other_boundary'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 6, 'timer')); + $state['history_after_prepare'] = $history($r1); + + // S1 delivered: the activity and S2's timer are fenced, then the barrier. + $state['deliver_s1_other_boundary'] = $route('deliver', $t2, $boundary($t2, $s1, $rq1, 6, 'timer')); + $state['deliver_s1'] = $route('deliver', $t2, $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['history_after_delivery'] = $taskHistory($t2); + $state['deliver_s1_again'] = $route('deliver', $t2, $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['history_after_redelivery'] = $history($r1); + $state['prepare_s1_after_delivery'] = $route('prepare', $t2, $boundary($t2, $s1, $rq1, 5, 'activity')); + $state['prepare_s2_after_effect'] = $route('prepare', $t2, $boundary($t2, $s2, $rq2, 6, 'timer')); + + // The activity worker sees the fence; the run request cannot acknowledge + // it, S1's request can, once. + $activityTask = '/api/worker/activity-tasks/'.($activity['task_id'] ?? 'missing'); + $state['activity_status_after_delivery'] = $raw('POST', $activityTask.'/status', $attempt); + $state['activity_heartbeat_after_delivery'] = $raw('POST', $activityTask.'/heartbeat', $attempt); + $state['activity_renew_after_delivery'] = $raw('POST', $activityTask.'/renew-lease', $attempt); + $state['acknowledge_with_run_request'] = $raw('POST', $activityTask.'/acknowledge-cancellation', + $attempt + ['request_id' => $runRequest['request_id'] ?? 'missing']); + $state['acknowledge_with_scope_request'] = $raw('POST', $activityTask.'/acknowledge-cancellation', $attempt + ['request_id' => $rq1]); + $state['acknowledge_with_scope_request_again'] = $raw('POST', $activityTask.'/acknowledge-cancellation', $attempt + ['request_id' => $rq1]); + $state['activity_status_after_acknowledgement'] = $raw('POST', $activityTask.'/status', $attempt); + + // Completion admission after the preparation: S1 and its unshielded + // descendant S2 are closed to new members; shielded S3 is not. + $state['claim_heartbeat_after_delivery'] = $heartbeat($t2); + $state['complete_in_prepared_scope'] = $complete($t2, [$timer($s1)]); + $state['complete_in_prepared_descendant'] = $complete($t2, [$timer($s2)]); + $state['complete_in_shielded_scope'] = $complete($t2, [$timer($s3)]); + $state['history_final'] = $history($r1); + + // ---- R2: a descendant prepared before its parent ---- + $queue2 = $workflowId.'-r2-queue'; + $worker2 = $workflowId.'-r2-worker'; + $register($worker2, $queue2, ['cooperative_cancellation']); + $r2 = $start('R2', $queue2); + $u1 = $poll($worker2, $queue2, 'R2.T1'); + $state['r2_open_s1'] = $route('open', $u1, $own($u1) + ['sequence' => 1]); + $p1 = $state['r2_open_s1']['response']['scope_id'] ?? 'missing-s1'; + $name($p1, 'R2.S1'); + $state['r2_open_s2'] = $route('open', $u1, $own($u1) + ['sequence' => 2, 'parent_scope_id' => $p1]); + $p2 = $state['r2_open_s2']['response']['scope_id'] ?? 'missing-s2'; + $name($p2, 'R2.S2'); + $state['r2_complete_members'] = $complete($u1, [$timer($p1), $timer($p2)]); + $alive($worker2); + $state['r2_request'] = $cancel($r2); + $r2Request = $state['r2_request']['response']['cancellation_request'] ?? []; + $name($r2Request['request_id'] ?? null, 'R2.request'); + $name($r2Request['requested_at'] ?? null, 'R2.requested_at'); + $name($r2Request['cleanup_deadline_at'] ?? null, 'R2.cleanup_deadline_at'); + $r2Requests = $requests($r2); + $name($r2Requests[$p1] ?? null, 'R2.S1.request'); + $name($r2Requests[$p2] ?? null, 'R2.S2.request'); + $u2 = $poll($worker2, $queue2, 'R2.T2'); + $state['r2_claim_after_request'] = $claimed($u2); + $pq1 = $r2Requests[$p1] ?? 'missing-request'; + $pq2 = $r2Requests[$p2] ?? 'missing-request'; + // S2 at its timer, then S1 at the next, unscheduled boundary. + $state['r2_prepare_s2'] = $route('prepare', $u2, $boundary($u2, $p2, $pq2, 4, 'timer')); + $state['r2_prepare_s1'] = $route('prepare', $u2, $boundary($u2, $p1, $pq1, 5, 'timer')); + $state['r2_complete_at_reserved_boundary'] = $complete($u2, [$timer(null)]); + $state['r2_deliver_s1_pending'] = $route('deliver', $u2, $boundary($u2, $p1, $pq1, 5, 'timer')); + $state['r2_history_after_pending'] = $history($r2); + $state['r2_deliver_s2'] = $route('deliver', $u2, $boundary($u2, $p2, $pq2, 4, 'timer')); + $state['r2_deliver_s1'] = $route('deliver', $u2, $boundary($u2, $p1, $pq1, 5, 'timer')); + $state['r2_history_after_deliveries'] = $taskHistory($u2); + // The delivered boundary consumed sequence 5: the next command is 6. + $state['r2_complete_after_delivery'] = $complete($u2, [$timer(null)]); + $state['r2_history'] = $history($r2); + + // ---- R3 and R4: claims that cannot open or prepare scopes ---- + $queue3 = $workflowId.'-r3-queue'; + $worker3 = $workflowId.'-r3-worker'; + $register($worker3, $queue3, []); + $start('R3', $queue3); + $v = $poll($worker3, $queue3, 'R3.T1'); + $state['plain_claim_open'] = $route('open', $v, $own($v) + ['sequence' => 1]); + $state['plain_claim_prepare'] = $route('prepare', $v, $boundary($v, 'NO-SUCH-SCOPE', 'NO-SUCH-REQUEST', 1, 'timer')); + $queue4 = $workflowId.'-r4-queue'; + $worker4 = $workflowId.'-r4-worker'; + $register($worker4, $queue4, ['cooperative_cancellation']); + $start('R4', $queue4); + $w = $poll($worker4, $queue4, 'R4.T1', '1.19'); + $state['legacy_claim_open'] = $route('open', $w, $own($w) + ['sequence' => 1]); + $state['legacy_claim_prepare'] = $route('prepare', $w, $boundary($w, 'NO-SUCH-SCOPE', 'NO-SUCH-REQUEST', 1, 'timer')); + + return cancellationScopesNormalize($state, $names); +} + +/** + * Replaces run-varying values with labels: known identities and instants by + * name, other ULIDs and SHA-256 digests by first appearance (object keys + * in sorted order), and other instants by their format. + * + * @param array $names + */ +function cancellationScopesNormalize(array $state, array $names): array +{ + $counters = ['ulid' => 0, 'sha256' => 0]; + $labels = $names; + $label = static function (string $kind, string $key) use (&$labels, &$counters): string { + if (! isset($labels[$key])) { + $labels[$key] = '{'.$kind.':'.(++$counters[$kind]).'}'; + } + + return $labels[$key]; + }; + $ulid = '[0-9A-HJKMNP-TV-Z]{26}'; + $walk = static function (mixed $value) use (&$walk, &$labels, $label, $ulid): mixed { + if (is_array($value)) { + // Object keys are visited in sorted order, so labels do not + // depend on the key order a server answers in. + if (! array_is_list($value)) { + ksort($value, SORT_STRING); + } + $out = []; + foreach ($value as $key => $item) { + $out[$key] = $walk($item); + } + + return $out; + } + if (! is_string($value)) { + return $value; + } + $upper = strtoupper($value); + if (isset($labels[$upper])) { + return $labels[$upper]; + } + if (preg_match('/^'.$ulid.'$/', $upper)) { + return $label('ulid', $upper); + } + if (preg_match('/^[0-9a-f]{64}$/', $value)) { + return $label('sha256', $value); + } + if (preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})$/', $value, $match)) { + return '{time:'.($match[2] === 'Z' ? 'Z' : 'offset').strlen($match[1] ?? '').'}'; + } + // `:` identities, such as wait ids. + if (preg_match('/^([a-z_]+):('.$ulid.')$/i', $value, $match)) { + return $match[1].':'.$label('ulid', strtoupper($match[2])); + } + + return $value; + }; + + return $walk($state); +} diff --git a/scripts/conformance/server-parity/contract.mjs b/scripts/conformance/server-parity/contract.mjs index 1cfd5a0ec..d76a45a35 100644 --- a/scripts/conformance/server-parity/contract.mjs +++ b/scripts/conformance/server-parity/contract.mjs @@ -19,6 +19,7 @@ import {checkWorkerPollFence} from './worker-poll-fence-contract.mjs'; import {checkWorkerSessions} from './worker-sessions-contract.mjs'; import {checkWorkerSessionActivities} from './worker-session-activities-contract.mjs'; import {checkActivityCancellationAcknowledgement} from './activity-cancellation-ack-contract.mjs'; +import {checkCancellationScopes} from './cancellation-scopes-contract.mjs'; import {checkServiceCatalog} from './service-catalog-contract.mjs'; import {checkWorkflowImport} from './workflow-import-contract.mjs'; import {checkWaterlineImport} from './waterline-import-contract.mjs'; @@ -168,6 +169,7 @@ export function checkObservation(fixture, observation, workflowId, artifacts) { const workerSessionActivities = fixture.worker_session_activities ? checkWorkerSessionActivities(fixture, observation, workflowId) : null; const activityCancellationAcknowledgement = fixture.activity_cancellation_acknowledgement ? checkActivityCancellationAcknowledgement(fixture, observation, workflowId) : null; + const cancellationScopes = fixture.cancellation_scopes ? checkCancellationScopes(fixture, observation, workflowId) : null; const serviceCatalog = fixture.service_catalog ? checkServiceCatalog(fixture, observation, workflowId) : null; const workflowImport = fixture.workflow_import ? checkWorkflowImport(fixture, observation, workflowId) : null; const waterlineImport = fixture.waterline_import ? checkWaterlineImport(fixture, observation, workflowId) : null; @@ -733,6 +735,7 @@ export function checkObservation(fixture, observation, workflowId, artifacts) { ...(workerSessions ? {worker_sessions: workerSessions} : {}), ...(workerSessionActivities ? {worker_session_activities: workerSessionActivities} : {}), ...(activityCancellationAcknowledgement ? {activity_cancellation_acknowledgement: activityCancellationAcknowledgement} : {}), + ...(cancellationScopes ? {cancellation_scopes: cancellationScopes} : {}), ...(serviceCatalog ? {service_catalog: serviceCatalog} : {}), ...(workflowImport ? {workflow_import: workflowImport} : {}), ...(waterlineImport ? {waterline_import: waterlineImport} : {}), diff --git a/scripts/conformance/server-parity/probe.php b/scripts/conformance/server-parity/probe.php index b19338945..c111becc9 100644 --- a/scripts/conformance/server-parity/probe.php +++ b/scripts/conformance/server-parity/probe.php @@ -438,6 +438,7 @@ function httpObservation(array $fixture, string $workflowId, string $namespace, $workerSessionActivities = isset($fixture['worker_session_activities']) ? finishHttpWorkerSessionActivities($client, $fixture, $workflowId, $url) : null; $activityCancellationAcknowledgement = isset($fixture['activity_cancellation_acknowledgement']) ? finishHttpActivityCancellationAcknowledgement($client, $fixture, $workflowId, $url) : null; + $cancellationScopes = isset($fixture['cancellation_scopes']) ? finishHttpCancellationScopes($client, $fixture, $workflowId, $url) : null; $serviceCatalog = isset($fixture['service_catalog']) ? finishHttpServiceCatalog($client, $fixture, $workflowId, $url) : null; $workflowImport = isset($fixture['workflow_import']) ? finishHttpWorkflowImport($client, $fixture, $workflowId, $url) : null; $waterlineImport = isset($fixture['waterline_import']) ? finishHttpWaterlineImport($client, $fixture, $workflowId, $url) : null; @@ -556,6 +557,7 @@ function httpObservation(array $fixture, string $workflowId, string $namespace, 'worker_sessions' => $workerSessions, 'worker_session_activities' => $workerSessionActivities, 'activity_cancellation_acknowledgement' => $activityCancellationAcknowledgement, + 'cancellation_scopes' => $cancellationScopes, 'service_catalog' => $serviceCatalog, 'workflow_import' => $workflowImport, 'waterline_import' => $waterlineImport, @@ -878,6 +880,7 @@ function embeddedObservation(array $fixture, string $workflowId, string $namespa require __DIR__.'/worker-sessions-probe.php'; require __DIR__.'/worker-session-activities-probe.php'; require __DIR__.'/activity-cancellation-ack-probe.php'; + require __DIR__.'/cancellation-scopes-probe.php'; require __DIR__.'/service-catalog-probe.php'; require __DIR__.'/workflow-import-probe.php'; require __DIR__.'/waterline-import-probe.php'; @@ -1069,6 +1072,9 @@ function embeddedObservation(array $fixture, string $workflowId, string $namespa if ($mode === 'embedded' && isset($fixture['activity_cancellation_acknowledgement'])) { $observation['activity_cancellation_acknowledgement'] = ['applicable' => false, 'reason' => 'embedded_has_no_worker_protocol']; } + if ($mode === 'embedded' && isset($fixture['cancellation_scopes'])) { + $observation['cancellation_scopes'] = ['applicable' => false, 'reason' => 'embedded_has_no_worker_protocol']; + } if ($mode === 'embedded' && isset($fixture['service_catalog'])) { $observation['service_catalog'] = ['applicable' => false, 'reason' => 'embedded_has_no_http_service_catalog']; } diff --git a/tests/Fixtures/ServerParity/cancellation-scopes.json b/tests/Fixtures/ServerParity/cancellation-scopes.json new file mode 100644 index 000000000..ff9e5540e --- /dev/null +++ b/tests/Fixtures/ServerParity/cancellation-scopes.json @@ -0,0 +1,7415 @@ +{ + "schema": "durable-workflow.server-parity-fixture/v1", + "id": "cancellation-scopes", + "review_state": "reviewed", + "workflow_type": "parity.v1.echo", + "input": { + "message": "cancellation scopes λ", + "count": 9007199254740993, + "optional": null + }, + "typed_value": { + "type": "map", + "value": { + "message": { + "type": "string", + "value": "cancellation scopes λ" + }, + "count": { + "type": "int64", + "value": "9007199254740993" + }, + "optional": { + "type": "null", + "value": null + } + } + }, + "activity": false, + "expected_events": [ + "StartAccepted", + "WorkflowStarted", + "WorkflowCompleted" + ], + "cancellation_scopes": { + "workflow_type": "parity.v1.cancel_scope", + "activity_type": "parity.v1.cancel_scope_activity", + "cleanup_timeout_seconds": 120, + "empty_blob": "wwHioz3/VYAiNwwA", + "published_defects": { + "root_scope_server_error": { + "summary": "Prepare and deliver with scope_id \"root\" reach Workflow's CancellationScopeRequests::assertScope, which throws root_scope_requires_run_cancellation. PortableCancellationScopeDelivery converts only cancellation_scope_* and cancellation_delivery_* reasons (and three named ones), so the exception escapes and published 2.11.1 answers Laravel's bare 500 {\"message\":\"Server Error\"} without the worker envelope. The intended answer is the delivery route's 409 refusal with that reason.", + "published": { + "prepare_root_scope": { + "response": { + "message": "Server Error" + }, + "status": 500 + }, + "deliver_root_scope": { + "response": { + "message": "Server Error" + }, + "status": 500 + } + } + } + }, + "expected": { + "applicable": true, + "namespace": "{id}-csc", + "discovery": { + "cooperative_cancellation": true, + "scope_capabilities": [] + }, + "open_protocol_missing": { + "response": { + "error": "Missing worker protocol version header.", + "protocol_version": "1.20", + "reason": "missing_protocol_version", + "remediation": "Send the X-Durable-Workflow-Protocol-Version: 1.20 header on worker protocol requests.", + "requested_version": null, + "server_capabilities": "{present}", + "supported_version": "1.20" + }, + "status": 400 + }, + "open_protocol_1_19": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_requires_protocol_1_20", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "open_protocol_1_21": { + "response": { + "error": "Unsupported worker protocol version.", + "protocol_version": "1.20", + "reason": "unsupported_protocol_version", + "remediation": "Worker requested protocol version 1.21; this server supports 1.20. Workers may target any 1.x version with x ≤ 20. Upgrade the worker to a release that targets a compatible version, or connect to a server that matches.", + "requested_version": "1.21", + "server_capabilities": "{present}", + "supported_version": "1.20" + }, + "status": 400 + }, + "open_missing_fields": { + "response": { + "errors": { + "lease_owner": [ + "The lease owner field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + }, + "message": "The lease owner field is required. (and 2 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "lease_owner": [ + "The lease owner field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + } + }, + "status": 422 + }, + "open_invalid_fields": { + "response": { + "errors": { + "lease_owner": [ + "The lease owner field must be a string." + ], + "parent_scope_id": [ + "The parent scope id field is required." + ], + "sequence": [ + "The sequence field must be an integer." + ], + "shield_parent": [ + "The shield parent field must be true or false." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + }, + "message": "The lease owner field must be a string. (and 4 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "lease_owner": [ + "The lease owner field must be a string." + ], + "parent_scope_id": [ + "The parent scope id field is required." + ], + "sequence": [ + "The sequence field must be an integer." + ], + "shield_parent": [ + "The shield parent field must be true or false." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + } + }, + "status": 422 + }, + "open_null_parent": { + "response": { + "errors": { + "parent_scope_id": [ + "The parent scope id field is required." + ], + "shield_parent": [ + "The shield parent field is required." + ] + }, + "message": "The parent scope id field is required. (and 1 more error)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "parent_scope_id": [ + "The parent scope id field is required." + ], + "shield_parent": [ + "The shield parent field is required." + ] + } + }, + "status": 422 + }, + "open_unknown_task": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "task_not_found", + "server_capabilities": "{present}" + }, + "status": 404 + }, + "open_unknown_namespace": { + "response": { + "message": "Namespace '{id}-csc-missing' does not exist.", + "namespace": "{id}-csc-missing", + "protocol_version": "1.20", + "reason": "namespace_not_found", + "remediation": "Register the namespace via POST /api/namespaces, or send an X-Namespace header naming an existing namespace.", + "server_capabilities": "{present}" + }, + "status": 404 + }, + "open_foreign_owner": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "lease_owner_mismatch", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "open_wrong_attempt": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "workflow_task_attempt_mismatch", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "open_sequence_ahead": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_sequence_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "open_unknown_parent": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_parent_not_recorded", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "open_s1": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1_again": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": true, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1_explicit_root": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": true, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1_shielded_replay": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_replay_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "open_s2": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:14}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "{R1.S1}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S2}", + "sequence": 2, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s3": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:18}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "{R1.S1}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S3}", + "sequence": 3, + "server_capabilities": "{present}", + "shield_parent": true, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s4": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:16}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "{R1.S2}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S4}", + "sequence": 4, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "complete_scoped_side_effect": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "invalid_cancellation_scope_command", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_unknown_scope": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "operation_scope_not_recorded", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_scope_protocol_1_19": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "cancellation_scope_membership_unavailable", + "recorded": false, + "requested_version": "1.19", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "unavailable": [ + "request_protocol" + ], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_members": { + "response": { + "created_task_ids": [ + "{R1.activity.task}", + "{ulid:6}", + "{ulid:7}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R1}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "history_after_members": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + } + ], + "request": { + "response": { + "accepted": true, + "cancellation_request": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}" + }, + "duplicate": false, + "reason": null, + "run_id": "{R1}", + "run_status": "waiting", + "workflow_id": "{id}-r1" + }, + "status": 202 + }, + "request_budget_seconds": 120, + "history_after_request": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + } + ], + "claim_after_request": { + "cancellation_request": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}" + }, + "lease_ten_seconds_from_poll": true, + "open_wait_id": "{absent}", + "resume_source_id": "{ulid:3}", + "resume_source_kind": "cancellation_scope_request", + "workflow_task_attempt": 1, + "workflow_wait_kind": "{absent}" + }, + "root_delivery_at_scoped_activity": { + "response": { + "call_kind": null, + "delivered": false, + "operation_sequence": null, + "operation_sequence_span": null, + "protocol_version": "1.20", + "reason": "cancellation_delivery_requires_scope", + "request_id": null, + "sequence": null, + "sequence_span": null, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "workflow_run_id": "{R1}" + }, + "status": 409 + }, + "prepare_protocol_1_19": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_requires_protocol_1_20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "deliver_protocol_1_19": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_requires_protocol_1_20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_missing_fields": { + "response": { + "errors": { + "call_kind": [ + "The call kind field is required." + ], + "lease_owner": [ + "The lease owner field is required." + ], + "request_id": [ + "The request id field is required." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + }, + "message": "The lease owner field is required. (and 5 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "call_kind": [ + "The call kind field is required." + ], + "lease_owner": [ + "The lease owner field is required." + ], + "request_id": [ + "The request id field is required." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + } + }, + "status": 422 + }, + "prepare_invalid_fields": { + "response": { + "errors": { + "call_kind": [ + "The selected call kind is invalid." + ], + "lease_owner": [ + "The lease owner field must be a string." + ], + "operation_sequence": [ + "The operation sequence field must be at least 1." + ], + "operation_sequence_span": [ + "The operation sequence span field must be at least 1." + ], + "request_id": [ + "The request id field must be a string." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field must be at least 1." + ], + "sequence_span": [ + "The sequence span field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + }, + "message": "The lease owner field must be a string. (and 8 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "call_kind": [ + "The selected call kind is invalid." + ], + "lease_owner": [ + "The lease owner field must be a string." + ], + "operation_sequence": [ + "The operation sequence field must be at least 1." + ], + "operation_sequence_span": [ + "The operation sequence span field must be at least 1." + ], + "request_id": [ + "The request id field must be a string." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field must be at least 1." + ], + "sequence_span": [ + "The sequence span field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + } + }, + "status": 422 + }, + "prepare_unknown_task": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "task_not_found", + "server_capabilities": "{present}", + "task_id": "no-such-task" + }, + "status": 404 + }, + "prepare_completed_claim": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_workflow_claim_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "prepare_foreign_owner": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "lease_owner_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_wrong_request": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_request_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_unknown_scope": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_not_recorded", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_shielded_scope": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_request_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_root_scope": { + "status": 409, + "response": { + "prepared": false, + "delivered": false, + "claim_released": false, + "task_id": "{R1.T2}", + "created_task_ids": [], + "reason": "root_scope_requires_run_cancellation", + "protocol_version": "1.20", + "server_capabilities": "{present}" + } + }, + "deliver_root_scope": { + "status": 409, + "response": { + "prepared": false, + "delivered": false, + "claim_released": false, + "task_id": "{R1.T2}", + "created_task_ids": [], + "reason": "root_scope_requires_run_cancellation", + "protocol_version": "1.20", + "server_capabilities": "{present}" + } + }, + "deliver_before_prepare": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_delivery_not_prepared", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_sequence_ahead": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_delivery_sequence_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_shape_mismatch": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_delivery_shape_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_activity_span": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "invalid_cancellation_delivery", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "open_after_request": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_run_not_active", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "open_replay_after_request": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": true, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T2}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "claim_heartbeat": 200, + "prepare_s1": { + "response": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:9}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "prepare_s1_again": { + "response": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:9}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "prepare_s1_other_boundary": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_delivery_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "history_after_prepare": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15 + } + ], + "deliver_s1_other_boundary": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_delivery_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "deliver_s1": { + "response": { + "activity_cancellations": [ + { + "abandoned": false, + "activity_execution_id": "{R1.activity}", + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "fenced": true, + "history_event_id": "{ulid:2}", + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "waiting_for_stop": false + } + ], + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:8}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:10}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "timer_members": [], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "history_after_delivery": [ + { + "event_type": "StartAccepted", + "sequence": 1, + "workflow_command_id": "{ulid:12}", + "workflow_task_id": null + }, + { + "event_type": "WorkflowStarted", + "sequence": 2, + "workflow_command_id": "{ulid:12}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "ActivityStarted", + "sequence": 10, + "workflow_command_id": null, + "workflow_task_id": "{R1.activity.task}" + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11, + "workflow_command_id": "{R1.request}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15, + "workflow_command_id": null, + "workflow_task_id": "{R1.T2}" + }, + { + "event_type": "ActivityCancelled", + "payload": { + "activity": { + "attempt_id": "{R1.activity.attempt}", + "cancellation_policy": "{absent}", + "cancellation_scope_id": "{R1.S1}", + "id": "{R1.activity}", + "sequence": 5, + "status": "cancelled" + }, + "activity_attempt": { + "activity_execution_id": "{R1.activity}", + "id": "{R1.activity.attempt}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "task_id": "{R1.activity.task}" + }, + "activity_attempt_id": "{R1.activity.attempt}", + "activity_class": "parity.v1.cancel_scope_activity", + "activity_execution_id": "{R1.activity}", + "activity_type": "parity.v1.cancel_scope_activity", + "attempt_number": 1, + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "type": "activity" + }, + "workflow_command_id": "{R1.S1.request}" + }, + "sequence": 16, + "workflow_command_id": "{R1.S1.request}", + "workflow_task_id": "{R1.activity.task}" + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:9}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 6, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:11}" + }, + "sequence": 17, + "workflow_command_id": null, + "workflow_task_id": "{R1.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 18, + "workflow_command_id": null, + "workflow_task_id": "{R1.T2}" + } + ], + "deliver_s1_again": { + "response": { + "activity_cancellations": [ + { + "abandoned": false, + "activity_execution_id": "{R1.activity}", + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "fenced": true, + "history_event_id": "{ulid:2}", + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "waiting_for_stop": false + } + ], + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:8}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:10}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "timer_members": [], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "history_after_redelivery": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15 + }, + { + "event_type": "ActivityCancelled", + "payload": { + "activity": { + "attempt_id": "{R1.activity.attempt}", + "cancellation_policy": "{absent}", + "cancellation_scope_id": "{R1.S1}", + "id": "{R1.activity}", + "sequence": 5, + "status": "cancelled" + }, + "activity_attempt": { + "activity_execution_id": "{R1.activity}", + "id": "{R1.activity.attempt}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "task_id": "{R1.activity.task}" + }, + "activity_attempt_id": "{R1.activity.attempt}", + "activity_class": "parity.v1.cancel_scope_activity", + "activity_execution_id": "{R1.activity}", + "activity_type": "parity.v1.cancel_scope_activity", + "attempt_number": 1, + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "type": "activity" + }, + "workflow_command_id": "{R1.S1.request}" + }, + "sequence": 16 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:9}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 6, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:11}" + }, + "sequence": 17 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 18 + } + ], + "prepare_s1_after_delivery": { + "response": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:9}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "prepare_s2_after_effect": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_preparation_after_effect", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "activity_status_after_delivery": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "activity_status": "cancelled", + "attempt_status": "cancelled", + "can_continue": false, + "cancel_requested": true, + "cancellation_acknowledgement": { + "acknowledged_at": null, + "callback_state": "unknown", + "cancellation_history_event_id": "{ulid:2}", + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "history_event_id": null, + "received_after_deadline": null, + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}" + }, + "deadlines": { + "start_to_close": "{time:Z6}" + }, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "{id}-r1-activity-worker", + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}", + "task_status": "cancelled", + "worker_session": null + }, + "status": 200 + }, + "activity_heartbeat_after_delivery": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "can_continue": false, + "cancel_requested": true, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "{id}-r1-activity-worker", + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}", + "worker_session": null + }, + "status": 200 + }, + "activity_renew_after_delivery": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "activity_execution_id": "{R1.activity}", + "activity_status": "cancelled", + "attempt_number": 1, + "attempt_status": "cancelled", + "can_continue": false, + "cancel_requested": true, + "heartbeat_deadline_at": null, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "{id}-r1-activity-worker", + "lease_renewed": false, + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "run_status": "waiting", + "server_capabilities": "{present}", + "task_status": "cancelled", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}", + "workflow_task_id": "{R1.activity.task}" + }, + "status": 409 + }, + "acknowledge_with_run_request": { + "response": { + "acknowledged": false, + "activity_attempt_id": "{R1.activity.attempt}", + "duplicate": false, + "heartbeat_recorded": false, + "history_event_id": null, + "lease_owner": "{id}-r1-activity-worker", + "protocol_version": "1.20", + "reason": "activity_cancellation_not_recorded", + "request_id": "{R1.request}", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}" + }, + "status": 409 + }, + "acknowledge_with_scope_request": { + "response": { + "acknowledged": true, + "activity_attempt_id": "{R1.activity.attempt}", + "duplicate": false, + "heartbeat_recorded": false, + "history_event_id": "{ulid:1}", + "lease_owner": "{id}-r1-activity-worker", + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}" + }, + "status": 200 + }, + "acknowledge_with_scope_request_again": { + "response": { + "acknowledged": true, + "activity_attempt_id": "{R1.activity.attempt}", + "duplicate": true, + "heartbeat_recorded": false, + "history_event_id": "{ulid:1}", + "lease_owner": "{id}-r1-activity-worker", + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}" + }, + "status": 200 + }, + "activity_status_after_acknowledgement": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "activity_status": "cancelled", + "attempt_status": "cancelled", + "can_continue": false, + "cancel_requested": true, + "cancellation_acknowledgement": { + "acknowledged_at": "{time:Z6}", + "callback_state": "stopped", + "cancellation_history_event_id": "{ulid:2}", + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "history_event_id": "{ulid:1}", + "received_after_deadline": false, + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}" + }, + "deadlines": { + "start_to_close": "{time:Z6}" + }, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "{id}-r1-activity-worker", + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}", + "task_status": "cancelled", + "worker_session": null + }, + "status": 200 + }, + "claim_heartbeat_after_delivery": 200, + "complete_in_prepared_scope": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "operation_scope_cancellation_prepared", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_in_prepared_descendant": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "operation_scope_cancellation_prepared", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_in_shielded_scope": { + "response": { + "created_task_ids": [ + "{ulid:4}", + "{ulid:5}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R1}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "history_final": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15 + }, + { + "event_type": "ActivityCancelled", + "payload": { + "activity": { + "attempt_id": "{R1.activity.attempt}", + "cancellation_policy": "{absent}", + "cancellation_scope_id": "{R1.S1}", + "id": "{R1.activity}", + "sequence": 5, + "status": "cancelled" + }, + "activity_attempt": { + "activity_execution_id": "{R1.activity}", + "id": "{R1.activity.attempt}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "task_id": "{R1.activity.task}" + }, + "activity_attempt_id": "{R1.activity.attempt}", + "activity_class": "parity.v1.cancel_scope_activity", + "activity_execution_id": "{R1.activity}", + "activity_type": "parity.v1.cancel_scope_activity", + "attempt_number": 1, + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "type": "activity" + }, + "workflow_command_id": "{R1.S1.request}" + }, + "sequence": 16 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:9}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 6, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:11}" + }, + "sequence": 17 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "{id}-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "{id}-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "{id}-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 18 + }, + { + "event_type": "ActivityCancellationAcknowledged", + "payload": { + "acknowledged_at": "{time:Z6}", + "activity_attempt_id": "{R1.activity.attempt}", + "activity_execution_id": "{R1.activity}", + "callback_state": "stopped", + "cancellation_history_event_id": "{ulid:2}", + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "evidence_source": "activity_worker", + "lease_owner": "{id}-r1-activity-worker", + "received_after_deadline": false, + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "{id}-r1-activity-worker", + "status": "cancelled", + "type": "activity" + } + }, + "sequence": 19 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 8 + }, + "sequence": 20 + } + ], + "r2_open_s1": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:35}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R2.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R2.T1}", + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_open_s2": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:33}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "opened": true, + "parent_scope_id": "{R2.S1}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R2.S2}", + "sequence": 2, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R2.T1}", + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_complete_members": { + "response": { + "created_task_ids": [ + "{ulid:22}", + "{ulid:23}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R2}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R2.T1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_request": { + "response": { + "accepted": true, + "cancellation_request": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}" + }, + "duplicate": false, + "reason": null, + "run_id": "{R2}", + "run_status": "waiting", + "workflow_id": "{id}-r2" + }, + "status": 202 + }, + "r2_claim_after_request": { + "cancellation_request": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}" + }, + "lease_ten_seconds_from_poll": true, + "open_wait_id": "{absent}", + "resume_source_id": "{ulid:19}", + "resume_source_kind": "cancellation_scope_request", + "workflow_task_attempt": 1, + "workflow_wait_kind": "{absent}" + }, + "r2_prepare_s2": { + "response": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:31}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_prepare_s1": { + "response": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:25}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_complete_at_reserved_boundary": { + "response": { + "created_task_ids": [], + "outcome": "completed", + "protocol_version": "1.20", + "reason": "operation_cancellation_delivery_reserved", + "recorded": false, + "run_id": "{R2}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "r2_deliver_s1_pending": { + "response": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "prepared": true, + "protocol_version": "1.20", + "reason": "cancellation_scope_descendant_delivery_pending", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "r2_history_after_pending": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R2.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 4 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S1}", + "sequence": 3 + }, + "sequence": 5 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S2}", + "sequence": 4 + }, + "sequence": 6 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 7 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "sequence": 8 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R2.S1}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "sequence": 9 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 10 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R2.S1}", + "propagation_history_event_id": "{ulid:32}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "scope_history_event_id": "{ulid:33}", + "scope_id": "{R2.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 11 + } + ], + "r2_deliver_s2": { + "response": { + "activity_cancellations": [], + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:30}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:28}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_deliver_s1": { + "response": { + "activity_cancellations": [], + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:24}", + "history_refresh_page_token": "MA==", + "lease_owner": "{id}-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:26}", + "sequence": 3, + "timer_id": "{ulid:27}" + }, + { + "fenced": true, + "history_event_id": "{ulid:28}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_history_after_deliveries": [ + { + "event_type": "StartAccepted", + "sequence": 1, + "workflow_command_id": "{ulid:34}", + "workflow_task_id": null + }, + { + "event_type": "WorkflowStarted", + "sequence": 2, + "workflow_command_id": "{ulid:34}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 3, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R2.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 4, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S1}", + "sequence": 3 + }, + "sequence": 5, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S2}", + "sequence": 4 + }, + "sequence": 6, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 7, + "workflow_command_id": "{R2.request}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "sequence": 8, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R2.S1}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "sequence": 9, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 10, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R2.S1}", + "propagation_history_event_id": "{ulid:32}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "scope_history_event_id": "{ulid:33}", + "scope_id": "{R2.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 11, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:31}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 4, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:29}" + }, + "sequence": 12, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 13, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:25}", + "request_history_event_id": "{ulid:19}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 3, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:27}" + }, + "sequence": 14, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 15, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + } + ], + "r2_complete_after_delivery": { + "response": { + "created_task_ids": [ + "{ulid:20}", + "{ulid:21}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R2}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_history": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R2.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 4 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S1}", + "sequence": 3 + }, + "sequence": 5 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S2}", + "sequence": 4 + }, + "sequence": 6 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 7 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "sequence": 8 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R2.S1}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "sequence": 9 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 10 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R2.S1}", + "propagation_history_event_id": "{ulid:32}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "scope_history_event_id": "{ulid:33}", + "scope_id": "{R2.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 11 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:31}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 4, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:29}" + }, + "sequence": 12 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 13 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:25}", + "request_history_event_id": "{ulid:19}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 3, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:27}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "{id}-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "{id}-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "{id}-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 15 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{absent}", + "sequence": 6 + }, + "sequence": 16 + } + ], + "plain_claim_open": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "plain_claim_prepare": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}", + "task_id": "{R3.T1}" + }, + "status": 409 + }, + "legacy_claim_open": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "legacy_claim_prepare": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}", + "task_id": "{R4.T1}" + }, + "status": 409 + } + } + } +} diff --git a/tests/Fixtures/ServerParityExpected/cancellation-scopes-observation.json b/tests/Fixtures/ServerParityExpected/cancellation-scopes-observation.json new file mode 100644 index 000000000..bbe9adaad --- /dev/null +++ b/tests/Fixtures/ServerParityExpected/cancellation-scopes-observation.json @@ -0,0 +1,7342 @@ +{ + "acknowledge_with_run_request": { + "response": { + "acknowledged": false, + "activity_attempt_id": "{R1.activity.attempt}", + "duplicate": false, + "heartbeat_recorded": false, + "history_event_id": null, + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "protocol_version": "1.20", + "reason": "activity_cancellation_not_recorded", + "request_id": "{R1.request}", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}" + }, + "status": 409 + }, + "acknowledge_with_scope_request": { + "response": { + "acknowledged": true, + "activity_attempt_id": "{R1.activity.attempt}", + "duplicate": false, + "heartbeat_recorded": false, + "history_event_id": "{ulid:1}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}" + }, + "status": 200 + }, + "acknowledge_with_scope_request_again": { + "response": { + "acknowledged": true, + "activity_attempt_id": "{R1.activity.attempt}", + "duplicate": true, + "heartbeat_recorded": false, + "history_event_id": "{ulid:1}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}" + }, + "status": 200 + }, + "activity_heartbeat_after_delivery": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "can_continue": false, + "cancel_requested": true, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}", + "worker_session": null + }, + "status": 200 + }, + "activity_renew_after_delivery": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "activity_execution_id": "{R1.activity}", + "activity_status": "cancelled", + "attempt_number": 1, + "attempt_status": "cancelled", + "can_continue": false, + "cancel_requested": true, + "heartbeat_deadline_at": null, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "lease_renewed": false, + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "run_status": "waiting", + "server_capabilities": "{present}", + "task_status": "cancelled", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}", + "workflow_task_id": "{R1.activity.task}" + }, + "status": 409 + }, + "activity_status_after_acknowledgement": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "activity_status": "cancelled", + "attempt_status": "cancelled", + "can_continue": false, + "cancel_requested": true, + "cancellation_acknowledgement": { + "acknowledged_at": "{time:Z6}", + "callback_state": "stopped", + "cancellation_history_event_id": "{ulid:2}", + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "history_event_id": "{ulid:1}", + "received_after_deadline": false, + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}" + }, + "deadlines": { + "start_to_close": "{time:Z6}" + }, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}", + "task_status": "cancelled", + "worker_session": null + }, + "status": 200 + }, + "activity_status_after_delivery": { + "response": { + "activity_attempt_id": "{R1.activity.attempt}", + "activity_status": "cancelled", + "attempt_status": "cancelled", + "can_continue": false, + "cancel_requested": true, + "cancellation_acknowledgement": { + "acknowledged_at": null, + "callback_state": "unknown", + "cancellation_history_event_id": "{ulid:2}", + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "history_event_id": null, + "received_after_deadline": null, + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}" + }, + "deadlines": { + "start_to_close": "{time:Z6}" + }, + "heartbeat_recorded": false, + "last_heartbeat_at": null, + "lease_expires_at": null, + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "protocol_version": "1.20", + "reason": "attempt_closed", + "run_closed_at": null, + "run_closed_reason": null, + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.activity.task}", + "task_status": "cancelled", + "worker_session": null + }, + "status": 200 + }, + "applicable": true, + "claim_after_request": { + "cancellation_request": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}" + }, + "lease_ten_seconds_from_poll": true, + "open_wait_id": "{absent}", + "resume_source_id": "{ulid:3}", + "resume_source_kind": "cancellation_scope_request", + "workflow_task_attempt": 1, + "workflow_wait_kind": "{absent}" + }, + "claim_heartbeat": 200, + "claim_heartbeat_after_delivery": 200, + "complete_in_prepared_descendant": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "operation_scope_cancellation_prepared", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_in_prepared_scope": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "operation_scope_cancellation_prepared", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_in_shielded_scope": { + "response": { + "created_task_ids": [ + "{ulid:4}", + "{ulid:5}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R1}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "complete_members": { + "response": { + "created_task_ids": [ + "{R1.activity.task}", + "{ulid:6}", + "{ulid:7}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R1}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "complete_scope_protocol_1_19": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "cancellation_scope_membership_unavailable", + "recorded": false, + "requested_version": "1.19", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "unavailable": [ + "request_protocol" + ], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_scoped_side_effect": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "invalid_cancellation_scope_command", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "complete_unknown_scope": { + "response": { + "minimum_protocol_version": "1.20", + "outcome": "rejected", + "protocol_version": "1.20", + "reason": "operation_scope_not_recorded", + "recorded": false, + "requested_version": "1.20", + "server_capabilities": "{present}", + "task_id": "{R1.T1}", + "unavailable": [], + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "deliver_before_prepare": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_delivery_not_prepared", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "deliver_protocol_1_19": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_requires_protocol_1_20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "deliver_root_scope": { + "response": { + "message": "Server Error" + }, + "status": 500 + }, + "deliver_s1": { + "response": { + "activity_cancellations": [ + { + "abandoned": false, + "activity_execution_id": "{R1.activity}", + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "fenced": true, + "history_event_id": "{ulid:2}", + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "waiting_for_stop": false + } + ], + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:8}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:10}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "timer_members": [], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "deliver_s1_again": { + "response": { + "activity_cancellations": [ + { + "abandoned": false, + "activity_execution_id": "{R1.activity}", + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "fenced": true, + "history_event_id": "{ulid:2}", + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "waiting_for_stop": false + } + ], + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:8}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:10}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "timer_members": [], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "deliver_s1_other_boundary": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_delivery_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "discovery": { + "cooperative_cancellation": true, + "scope_capabilities": [] + }, + "history_after_delivery": [ + { + "event_type": "StartAccepted", + "sequence": 1, + "workflow_command_id": "{ulid:12}", + "workflow_task_id": null + }, + { + "event_type": "WorkflowStarted", + "sequence": 2, + "workflow_command_id": "{ulid:12}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9, + "workflow_command_id": null, + "workflow_task_id": "{R1.T1}" + }, + { + "event_type": "ActivityStarted", + "sequence": 10, + "workflow_command_id": null, + "workflow_task_id": "{R1.activity.task}" + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11, + "workflow_command_id": "{R1.request}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15, + "workflow_command_id": null, + "workflow_task_id": "{R1.T2}" + }, + { + "event_type": "ActivityCancelled", + "payload": { + "activity": { + "attempt_id": "{R1.activity.attempt}", + "cancellation_policy": "{absent}", + "cancellation_scope_id": "{R1.S1}", + "id": "{R1.activity}", + "sequence": 5, + "status": "cancelled" + }, + "activity_attempt": { + "activity_execution_id": "{R1.activity}", + "id": "{R1.activity.attempt}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "task_id": "{R1.activity.task}" + }, + "activity_attempt_id": "{R1.activity.attempt}", + "activity_class": "parity.v1.cancel_scope_activity", + "activity_execution_id": "{R1.activity}", + "activity_type": "parity.v1.cancel_scope_activity", + "attempt_number": 1, + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "type": "activity" + }, + "workflow_command_id": "{R1.S1.request}" + }, + "sequence": 16, + "workflow_command_id": "{R1.S1.request}", + "workflow_task_id": "{R1.activity.task}" + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:9}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 6, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:11}" + }, + "sequence": 17, + "workflow_command_id": null, + "workflow_task_id": "{R1.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 18, + "workflow_command_id": null, + "workflow_task_id": "{R1.T2}" + } + ], + "history_after_members": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + } + ], + "history_after_prepare": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15 + } + ], + "history_after_redelivery": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15 + }, + { + "event_type": "ActivityCancelled", + "payload": { + "activity": { + "attempt_id": "{R1.activity.attempt}", + "cancellation_policy": "{absent}", + "cancellation_scope_id": "{R1.S1}", + "id": "{R1.activity}", + "sequence": 5, + "status": "cancelled" + }, + "activity_attempt": { + "activity_execution_id": "{R1.activity}", + "id": "{R1.activity.attempt}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "task_id": "{R1.activity.task}" + }, + "activity_attempt_id": "{R1.activity.attempt}", + "activity_class": "parity.v1.cancel_scope_activity", + "activity_execution_id": "{R1.activity}", + "activity_type": "parity.v1.cancel_scope_activity", + "attempt_number": 1, + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "type": "activity" + }, + "workflow_command_id": "{R1.S1.request}" + }, + "sequence": 16 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:9}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 6, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:11}" + }, + "sequence": 17 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 18 + } + ], + "history_after_request": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + } + ], + "history_final": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 4 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S3}", + "sequence": 3, + "shield_parent": true, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 5 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R1.S2}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R1.S4}", + "sequence": 4, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R1.T1}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 6 + }, + { + "event_type": "ActivityScheduled", + "payload": { + "activity.cancellation_policy": "{absent}", + "activity.cancellation_scope_id": "{R1.S1}", + "cancellation_scope_id": "{absent}", + "sequence": 5 + }, + "sequence": 7 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S2}", + "sequence": 6 + }, + "sequence": 8 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 7 + }, + "sequence": 9 + }, + { + "event_type": "ActivityStarted", + "sequence": 10 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 11 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "sequence": 12 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S1}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "sequence": 13 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R1.S2}", + "request_id": "{R1.S4.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R1.S4}", + "workflow_run_id": "{R1}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S1}", + "propagation_history_event_id": "{ulid:13}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "scope_history_event_id": "{ulid:14}", + "scope_id": "{R1.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:2}", + "sequence": 6, + "timer_id": "{ulid:11}" + } + ], + "wait_members": [] + }, + { + "activity_members": [], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S4.request}", + "scope_id": "{R1.S4}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R1.S2}", + "propagation_history_event_id": "{ulid:15}", + "request_history_event_id": "{ulid:15}", + "request_id": "{R1.S4.request}", + "scope_history_event_id": "{ulid:16}", + "scope_id": "{R1.S4}", + "timer_members": [], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}" + }, + "sequence": 15 + }, + { + "event_type": "ActivityCancelled", + "payload": { + "activity": { + "attempt_id": "{R1.activity.attempt}", + "cancellation_policy": "{absent}", + "cancellation_scope_id": "{R1.S1}", + "id": "{R1.activity}", + "sequence": 5, + "status": "cancelled" + }, + "activity_attempt": { + "activity_execution_id": "{R1.activity}", + "id": "{R1.activity.attempt}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "task_id": "{R1.activity.task}" + }, + "activity_attempt_id": "{R1.activity.attempt}", + "activity_class": "parity.v1.cancel_scope_activity", + "activity_execution_id": "{R1.activity}", + "activity_type": "parity.v1.cancel_scope_activity", + "attempt_number": 1, + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "request_history_event_id": "{ulid:3}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.activity-scope-cancellation/v1", + "scope_id": "{R1.S1}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "type": "activity" + }, + "workflow_command_id": "{R1.S1.request}" + }, + "sequence": 16 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S2.request}", + "scope_id": "{R1.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:9}", + "request_history_event_id": "{ulid:13}", + "request_id": "{R1.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R1.S2}", + "workflow_run_id": "{R1}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 6, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:11}" + }, + "sequence": 17 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "request_id": "{R1.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R1.T2}", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R1}" + }, + "sequence": 18 + }, + { + "event_type": "ActivityCancellationAcknowledged", + "payload": { + "acknowledged_at": "{time:Z6}", + "activity_attempt_id": "{R1.activity.attempt}", + "activity_execution_id": "{R1.activity}", + "callback_state": "stopped", + "cancellation_history_event_id": "{ulid:2}", + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "evidence_source": "activity_worker", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "received_after_deadline": false, + "request_id": "{R1.S1.request}", + "root_request_id": "{R1.request}", + "sequence": 5, + "task": { + "attempt_count": 1, + "id": "{R1.activity.task}", + "lease_owner": "pr1-cancellation-scopes-r1-activity-worker", + "status": "cancelled", + "type": "activity" + } + }, + "sequence": 19 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R1.S3}", + "sequence": 8 + }, + "sequence": 20 + } + ], + "legacy_claim_open": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "legacy_claim_prepare": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}", + "task_id": "{R4.T1}" + }, + "status": 409 + }, + "namespace": "pr1-cancellation-scopes-csc", + "open_after_request": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_run_not_active", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "open_foreign_owner": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "lease_owner_mismatch", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "open_invalid_fields": { + "response": { + "errors": { + "lease_owner": [ + "The lease owner field must be a string." + ], + "parent_scope_id": [ + "The parent scope id field is required." + ], + "sequence": [ + "The sequence field must be an integer." + ], + "shield_parent": [ + "The shield parent field must be true or false." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + }, + "message": "The lease owner field must be a string. (and 4 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "lease_owner": [ + "The lease owner field must be a string." + ], + "parent_scope_id": [ + "The parent scope id field is required." + ], + "sequence": [ + "The sequence field must be an integer." + ], + "shield_parent": [ + "The shield parent field must be true or false." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + } + }, + "status": 422 + }, + "open_missing_fields": { + "response": { + "errors": { + "lease_owner": [ + "The lease owner field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + }, + "message": "The lease owner field is required. (and 2 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "lease_owner": [ + "The lease owner field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + } + }, + "status": 422 + }, + "open_null_parent": { + "response": { + "errors": { + "parent_scope_id": [ + "The parent scope id field is required." + ], + "shield_parent": [ + "The shield parent field is required." + ] + }, + "message": "The parent scope id field is required. (and 1 more error)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "parent_scope_id": [ + "The parent scope id field is required." + ], + "shield_parent": [ + "The shield parent field is required." + ] + } + }, + "status": 422 + }, + "open_protocol_1_19": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_requires_protocol_1_20", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "open_protocol_1_21": { + "response": { + "error": "Unsupported worker protocol version.", + "protocol_version": "1.20", + "reason": "unsupported_protocol_version", + "remediation": "Worker requested protocol version 1.21; this server supports 1.20. Workers may target any 1.x version with x ≤ 20. Upgrade the worker to a release that targets a compatible version, or connect to a server that matches.", + "requested_version": "1.21", + "server_capabilities": "{present}", + "supported_version": "1.20" + }, + "status": 400 + }, + "open_protocol_missing": { + "response": { + "error": "Missing worker protocol version header.", + "protocol_version": "1.20", + "reason": "missing_protocol_version", + "remediation": "Send the X-Durable-Workflow-Protocol-Version: 1.20 header on worker protocol requests.", + "requested_version": null, + "server_capabilities": "{present}", + "supported_version": "1.20" + }, + "status": 400 + }, + "open_replay_after_request": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": true, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T2}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1_again": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": true, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1_explicit_root": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": true, + "history_event_id": "{ulid:17}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s1_shielded_replay": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_replay_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "open_s2": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:14}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "{R1.S1}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S2}", + "sequence": 2, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s3": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:18}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "{R1.S1}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S3}", + "sequence": 3, + "server_capabilities": "{present}", + "shield_parent": true, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_s4": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:16}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "opened": true, + "parent_scope_id": "{R1.S2}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R1.S4}", + "sequence": 4, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R1.T1}", + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "open_sequence_ahead": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_sequence_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "open_unknown_namespace": { + "response": { + "message": "Namespace 'pr1-cancellation-scopes-csc-missing' does not exist.", + "namespace": "pr1-cancellation-scopes-csc-missing", + "protocol_version": "1.20", + "reason": "namespace_not_found", + "remediation": "Register the namespace via POST /api/namespaces, or send an X-Namespace header naming an existing namespace.", + "server_capabilities": "{present}" + }, + "status": 404 + }, + "open_unknown_parent": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "opened": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_parent_not_recorded", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "open_unknown_task": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "task_not_found", + "server_capabilities": "{present}" + }, + "status": 404 + }, + "open_wrong_attempt": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "workflow_task_attempt_mismatch", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "plain_claim_open": { + "response": { + "opened": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}" + }, + "status": 409 + }, + "plain_claim_prepare": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "active_claim_cancellation_not_supported", + "server_capabilities": "{present}", + "task_id": "{R3.T1}" + }, + "status": 409 + }, + "prepare_activity_span": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "invalid_cancellation_delivery", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_completed_claim": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_workflow_claim_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T1}" + }, + "status": 409 + }, + "prepare_foreign_owner": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "lease_owner_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_invalid_fields": { + "response": { + "errors": { + "call_kind": [ + "The selected call kind is invalid." + ], + "lease_owner": [ + "The lease owner field must be a string." + ], + "operation_sequence": [ + "The operation sequence field must be at least 1." + ], + "operation_sequence_span": [ + "The operation sequence span field must be at least 1." + ], + "request_id": [ + "The request id field must be a string." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field must be at least 1." + ], + "sequence_span": [ + "The sequence span field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + }, + "message": "The lease owner field must be a string. (and 8 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "call_kind": [ + "The selected call kind is invalid." + ], + "lease_owner": [ + "The lease owner field must be a string." + ], + "operation_sequence": [ + "The operation sequence field must be at least 1." + ], + "operation_sequence_span": [ + "The operation sequence span field must be at least 1." + ], + "request_id": [ + "The request id field must be a string." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field must be at least 1." + ], + "sequence_span": [ + "The sequence span field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field must be at least 1." + ] + } + }, + "status": 422 + }, + "prepare_missing_fields": { + "response": { + "errors": { + "call_kind": [ + "The call kind field is required." + ], + "lease_owner": [ + "The lease owner field is required." + ], + "request_id": [ + "The request id field is required." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + }, + "message": "The lease owner field is required. (and 5 more errors)", + "protocol_version": "1.20", + "reason": "validation_failed", + "server_capabilities": "{present}", + "validation_errors": { + "call_kind": [ + "The call kind field is required." + ], + "lease_owner": [ + "The lease owner field is required." + ], + "request_id": [ + "The request id field is required." + ], + "scope_id": [ + "The scope id field is required." + ], + "sequence": [ + "The sequence field is required." + ], + "workflow_task_attempt": [ + "The workflow task attempt field is required." + ] + } + }, + "status": 422 + }, + "prepare_protocol_1_19": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_requires_protocol_1_20", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_root_scope": { + "response": { + "message": "Server Error" + }, + "status": 500 + }, + "prepare_s1": { + "response": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:9}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "prepare_s1_after_delivery": { + "response": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:9}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "prepare_s1_again": { + "response": { + "activity_members": [ + { + "activity_execution_id": "{R1.activity}", + "descriptor_hash": "{sha256:1}", + "sequence": 5 + } + ], + "authority_deadline_at": "{R1.cleanup_deadline_at}", + "call_kind": "activity", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + }, + { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R1.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r1", + "workflow_run_id": "{R1}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R1.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r1", + "root_workflow_run_id": "{R1}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:9}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r1-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:9}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R1.S1.request}", + "scope_id": "{R1.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "timer_members": [], + "wait_members": [], + "workflow_run_id": "{R1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "prepare_s1_other_boundary": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_delivery_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_s2_after_effect": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_preparation_after_effect", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_sequence_ahead": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_delivery_sequence_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_shape_mismatch": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_delivery_shape_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_shielded_scope": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_request_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_unknown_scope": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_not_recorded", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "prepare_unknown_task": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "task_not_found", + "server_capabilities": "{present}", + "task_id": "no-such-task" + }, + "status": 404 + }, + "prepare_wrong_request": { + "response": { + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "prepared": false, + "protocol_version": "1.20", + "reason": "cancellation_scope_request_mismatch", + "server_capabilities": "{present}", + "task_id": "{R1.T2}" + }, + "status": 409 + }, + "r2_claim_after_request": { + "cancellation_request": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}" + }, + "lease_ten_seconds_from_poll": true, + "open_wait_id": "{absent}", + "resume_source_id": "{ulid:19}", + "resume_source_kind": "cancellation_scope_request", + "workflow_task_attempt": 1, + "workflow_wait_kind": "{absent}" + }, + "r2_complete_after_delivery": { + "response": { + "created_task_ids": [ + "{ulid:20}", + "{ulid:21}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R2}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_complete_at_reserved_boundary": { + "response": { + "created_task_ids": [], + "outcome": "completed", + "protocol_version": "1.20", + "reason": "operation_cancellation_delivery_reserved", + "recorded": false, + "run_id": "{R2}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "r2_complete_members": { + "response": { + "created_task_ids": [ + "{ulid:22}", + "{ulid:23}" + ], + "outcome": "completed", + "protocol_version": "1.20", + "reason": null, + "recorded": true, + "run_id": "{R2}", + "run_status": "waiting", + "server_capabilities": "{present}", + "task_id": "{R2.T1}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_deliver_s1": { + "response": { + "activity_cancellations": [], + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:24}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:26}", + "sequence": 3, + "timer_id": "{ulid:27}" + }, + { + "fenced": true, + "history_event_id": "{ulid:28}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_deliver_s1_pending": { + "response": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "prepared": true, + "protocol_version": "1.20", + "reason": "cancellation_scope_descendant_delivery_pending", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 409 + }, + "r2_deliver_s2": { + "response": { + "activity_cancellations": [], + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_cancellations": [], + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": true, + "history_event_id": "{ulid:30}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_cancellations": [ + { + "fenced": true, + "history_event_id": "{ulid:28}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_cancellations": [], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_history": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R2.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 4 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S1}", + "sequence": 3 + }, + "sequence": 5 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S2}", + "sequence": 4 + }, + "sequence": 6 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 7 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "sequence": 8 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R2.S1}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "sequence": 9 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 10 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R2.S1}", + "propagation_history_event_id": "{ulid:32}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "scope_history_event_id": "{ulid:33}", + "scope_id": "{R2.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 11 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:31}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 4, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:29}" + }, + "sequence": 12 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 13 + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:25}", + "request_history_event_id": "{ulid:19}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 3, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:27}" + }, + "sequence": 14 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 15 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{absent}", + "sequence": 6 + }, + "sequence": 16 + } + ], + "r2_history_after_deliveries": [ + { + "event_type": "StartAccepted", + "sequence": 1, + "workflow_command_id": "{ulid:34}", + "workflow_task_id": null + }, + { + "event_type": "WorkflowStarted", + "sequence": 2, + "workflow_command_id": "{ulid:34}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 3, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R2.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 4, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S1}", + "sequence": 3 + }, + "sequence": 5, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S2}", + "sequence": 4 + }, + "sequence": 6, + "workflow_command_id": null, + "workflow_task_id": "{R2.T1}" + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 7, + "workflow_command_id": "{R2.request}", + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "sequence": 8, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R2.S1}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "sequence": 9, + "workflow_command_id": null, + "workflow_task_id": null + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 10, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R2.S1}", + "propagation_history_event_id": "{ulid:32}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "scope_history_event_id": "{ulid:33}", + "scope_id": "{R2.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 11, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:31}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 4, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:29}" + }, + "sequence": 12, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 13, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "event_type": "TimerCancelled", + "payload": { + "cancellation_scope": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "preparation_history_event_id": "{ulid:25}", + "request_history_event_id": "{ulid:19}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.scoped-timer-cancellation/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "cancelled_at": "{time:Z6}", + "delay_seconds": 3600, + "fire_at": "{time:Z6}", + "sequence": 3, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_id": "{ulid:27}" + }, + "sequence": 14, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDelivered", + "payload": { + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-delivery/v1", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 15, + "workflow_command_id": null, + "workflow_task_id": "{R2.T2}" + } + ], + "r2_history_after_pending": [ + { + "event_type": "StartAccepted", + "sequence": 1 + }, + { + "event_type": "WorkflowStarted", + "sequence": 2 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "root", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S1}", + "sequence": 1, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 3 + }, + { + "event_type": "CancellationScopeOpened", + "payload": { + "parent_scope_id": "{R2.S1}", + "schema": "durable-workflow.cancellation-scope/v1", + "scope_id": "{R2.S2}", + "sequence": 2, + "shield_parent": false, + "task": { + "attempt_count": 1, + "id": "{R2.T1}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "workflow_run_id": "{R2}" + }, + "sequence": 4 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S1}", + "sequence": 3 + }, + "sequence": 5 + }, + { + "event_type": "TimerScheduled", + "payload": { + "cancellation_scope_id": "{R2.S2}", + "sequence": 4 + }, + "sequence": 6 + }, + { + "event_type": "CooperativeCancellationRequested", + "sequence": 7 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "root", + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S1}", + "workflow_run_id": "{R2}" + }, + "sequence": 8 + }, + { + "event_type": "CancellationScopeRequested", + "payload": { + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "parent_scope_id": "{R2.S1}", + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-request/v1", + "scope_id": "{R2.S2}", + "workflow_run_id": "{R2}" + }, + "sequence": 9 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S2.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 10 + }, + { + "claim_lease_within_ten_seconds": true, + "event_type": "CancellationScopeDeliveryPrepared", + "payload": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "descendant_members": [ + { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "parent_scope_id": "{R2.S1}", + "propagation_history_event_id": "{ulid:32}", + "request_history_event_id": "{ulid:32}", + "request_id": "{R2.S2.request}", + "scope_history_event_id": "{ulid:33}", + "scope_id": "{R2.S2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [] + } + ], + "operation_sequence": null, + "operation_sequence_span": 1, + "request_id": "{R2.S1.request}", + "schema": "durable-workflow.cancellation-scope-preparation/v5", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "task": { + "attempt_count": 1, + "id": "{R2.T2}", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "status": "leased", + "type": "workflow" + }, + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}" + }, + "sequence": 11 + } + ], + "r2_open_s1": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:35}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "opened": true, + "parent_scope_id": "root", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R2.S1}", + "sequence": 1, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R2.T1}", + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_open_s2": { + "response": { + "claim_released": false, + "created_task_ids": [], + "duplicate": false, + "history_event_id": "{ulid:33}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "opened": true, + "parent_scope_id": "{R2.S1}", + "protocol_version": "1.20", + "reason": null, + "scope_id": "{R2.S2}", + "sequence": 2, + "server_capabilities": "{present}", + "shield_parent": false, + "task_id": "{R2.T1}", + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_prepare_s1": { + "response": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:25}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:25}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "sequence": 5, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:3}", + "sequence": 3, + "timer_id": "{ulid:27}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_prepare_s2": { + "response": { + "activity_members": [], + "authority_deadline_at": "{R2.cleanup_deadline_at}", + "call_kind": "timer", + "cancellation": { + "lineage": [ + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.request}", + "scope_id": "root", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S1.request}", + "scope_id": "{R2.S1}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + }, + { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "root_context": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "lineage": [ + { + "request_id": "{R2.request}", + "workflow_instance_id": "pr1-cancellation-scopes-r2", + "workflow_run_id": "{R2}" + } + ], + "parent_request_id": null, + "reason": "cancellation scopes fixture λ", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}", + "requester": { + "id": "legacy-token", + "label": "Admin", + "type": "auth:token" + }, + "root_request_id": "{R2.request}", + "root_workflow_instance_id": "pr1-cancellation-scopes-r2", + "root_workflow_run_id": "{R2}", + "schema": "durable-workflow.cancellation-context/v1", + "source": "control_plane" + }, + "schema": "durable-workflow.scoped-cancellation-context/v1" + }, + "child_members": [], + "claim_released": false, + "created_task_ids": [], + "delivered": false, + "history_event_id": "{ulid:31}", + "history_refresh_page_token": "MA==", + "lease_owner": "pr1-cancellation-scopes-r2-worker", + "operation_sequence": null, + "operation_sequence_span": 1, + "preparation_history_event_id": "{ulid:31}", + "prepared": true, + "protocol_version": "1.20", + "reason": null, + "request_id": "{R2.S2.request}", + "scope_id": "{R2.S2}", + "sequence": 4, + "sequence_span": 1, + "server_capabilities": "{present}", + "task_id": "{R2.T2}", + "timer_members": [ + { + "descriptor_hash": "{sha256:4}", + "sequence": 4, + "timer_id": "{ulid:29}" + } + ], + "wait_members": [], + "workflow_run_id": "{R2}", + "workflow_task_attempt": 1 + }, + "status": 200 + }, + "r2_request": { + "response": { + "accepted": true, + "cancellation_request": { + "cleanup_deadline_at": "{R2.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R2.request}", + "requested_at": "{R2.requested_at}" + }, + "duplicate": false, + "reason": null, + "run_id": "{R2}", + "run_status": "waiting", + "workflow_id": "pr1-cancellation-scopes-r2" + }, + "status": 202 + }, + "request": { + "response": { + "accepted": true, + "cancellation_request": { + "cleanup_deadline_at": "{R1.cleanup_deadline_at}", + "delivered_at": null, + "delivery_sequence": null, + "history_refresh_page_token": "MA==", + "request_id": "{R1.request}", + "requested_at": "{R1.requested_at}" + }, + "duplicate": false, + "reason": null, + "run_id": "{R1}", + "run_status": "waiting", + "workflow_id": "pr1-cancellation-scopes-r1" + }, + "status": 202 + }, + "request_budget_seconds": 120, + "root_delivery_at_scoped_activity": { + "response": { + "call_kind": null, + "delivered": false, + "operation_sequence": null, + "operation_sequence_span": null, + "protocol_version": "1.20", + "reason": "cancellation_delivery_requires_scope", + "request_id": null, + "sequence": null, + "sequence_span": null, + "server_capabilities": "{present}", + "task_id": "{R1.T2}", + "workflow_run_id": "{R1}" + }, + "status": 409 + } +} diff --git a/tests/Unit/ServerParityCancellationScopesContractTest.mjs b/tests/Unit/ServerParityCancellationScopesContractTest.mjs new file mode 100644 index 000000000..26321056c --- /dev/null +++ b/tests/Unit/ServerParityCancellationScopesContractTest.mjs @@ -0,0 +1,105 @@ +import assert from 'node:assert/strict'; +import {readFileSync} from 'node:fs'; +import test from 'node:test'; +import {checkCancellationScopes} from '../../scripts/conformance/server-parity/cancellation-scopes-contract.mjs'; + +const fixture = JSON.parse(readFileSync(new URL('../Fixtures/ServerParity/cancellation-scopes.json', import.meta.url))); +// The observation published Server 2.11.1 returned for this fixture. +const recorded = JSON.parse(readFileSync( + new URL('../Fixtures/ServerParityExpected/cancellation-scopes-observation.json', import.meta.url))); +const id = 'pr1-cancellation-scopes'; +const observation = (change = () => {}, base = recorded) => { + const state = structuredClone(base); + change(state); + return {mode: 'http', cancellation_scopes: state}; +}; +const check = state => checkCancellationScopes(fixture, state, id); +// The intended answers: a root scope refused with the delivery route's 409. +const intended = JSON.parse(JSON.stringify(fixture.cancellation_scopes.expected).replaceAll('{id}', id)); +const event = (history, type) => history.find(item => item.event_type === type); + +test('the published 2.11.1 recording satisfies the contract', () => { + assert.deepStrictEqual(check(observation()), {scope: 'cancellation_scopes'}); +}); + +test('the intended root scope refusal is accepted too', () => { + assert.deepStrictEqual(check(observation(() => {}, intended)), {scope: 'cancellation_scopes'}); + assert.equal(intended.prepare_root_scope.status, 409); + assert.equal(intended.deliver_root_scope.response.reason, 'root_scope_requires_run_cancellation'); +}); + +test('embedded applications are explicitly inapplicable', () => { + assert.deepStrictEqual(check({mode: 'embedded', cancellation_scopes: + {applicable: false, reason: 'embedded_has_no_worker_protocol'}}), {scope: 'cancellation_scopes'}); +}); + +for (const [label, change, message] of [ + // Native answered 501 for the scope routes before this fixture. + ['an unimplemented route', state => { + state.open_s1 = {status: 501, response: {reason: 'rust_capability_not_implemented'}}; + }, /cancellation scopes open_s1/], + ['only part of the published root scope defect', state => { + state.prepare_root_scope = structuredClone(intended.prepare_root_scope); + }, /cancellation scopes deliver_root_scope/], + ['a root scope refused with another reason', state => { + for (const step of ['prepare_root_scope', 'deliver_root_scope']) { + state[step] = structuredClone(intended[step]); + state[step].response.reason = 'cancellation_scope_not_recorded'; + } + }, /cancellation scopes prepare_root_scope/], + ['a replayed open that records a second scope', state => { + state.open_s1_again.response.duplicate = false; + }, /cancellation scopes open_s1_again/], + ['a shielded scope requested with its parent', state => { + const requested = state.history_after_request.filter(item => item.event_type === 'CancellationScopeRequested'); + requested.push({...structuredClone(requested[0]), payload: {...requested[0].payload, scope_id: '{R1.S3}'}}); + state.history_after_request = [...state.history_after_request.filter(item => item.event_type !== 'CancellationScopeRequested'), + ...requested]; + }, /cancellation scopes history_after_request/], + ['a run request that leaves the run-level task instead of the scope task', state => { + state.claim_after_request.resume_source_kind = 'cancellation_request'; + }, /cancellation scopes claim_after_request/], + ['a claim lease that is not the cleanup lease', state => { + state.claim_after_request.lease_ten_seconds_from_poll = false; + }, /cancellation scopes claim_after_request/], + ['a delivery that skips the frozen descendant timer', state => { + state.deliver_s1.response.timer_cancellations = []; + }, /cancellation scopes deliver_s1/], + ['a redelivery that records again', state => { + state.history_after_redelivery.push(structuredClone(event(state.history_after_redelivery, 'CancellationScopeDelivered'))); + }, /cancellation scopes history_after_redelivery/], + ['an activity fence recorded under the run request', state => { + event(state.history_after_delivery, 'ActivityCancelled').workflow_command_id = '{R1.request}'; + }, /cancellation scopes history_after_delivery/], + ['a scope fence acknowledged by the run request', state => { + state.acknowledge_with_run_request = structuredClone(state.acknowledge_with_scope_request); + }, /cancellation scopes acknowledge_with_run_request/], + ['a new member admitted into a prepared scope', state => { + state.complete_in_prepared_scope = structuredClone(state.complete_in_shielded_scope); + }, /cancellation scopes complete_in_prepared_scope/], + ['a shield that does not stop the prepared walk', state => { + state.complete_in_shielded_scope = structuredClone(state.complete_in_prepared_descendant); + }, /cancellation scopes complete_in_shielded_scope/], + ['a parent delivered past its pending descendant', state => { + state.r2_deliver_s1_pending = structuredClone(state.r2_deliver_s1); + }, /cancellation scopes r2_deliver_s1_pending/], + ['a root command at a reserved boundary', state => { + state.r2_complete_at_reserved_boundary.status = 200; + }, /cancellation scopes r2_complete_at_reserved_boundary/], + ['a delivered boundary that does not consume its sequence', state => { + state.r2_history.at(-1).payload.sequence = 5; + }, /cancellation scopes r2_history/], + ['a capability-less claim that opens a scope', state => { + state.plain_claim_open = structuredClone(state.open_s1); + }, /cancellation scopes plain_claim_open/], + ['a missing step', state => { + delete state.prepare_shielded_scope; + }, /cancellation scopes prepare_shielded_scope/], + ['an extra step', state => { + state.extra = {status: 200}; + }, /cancellation scopes steps/], +]) { + test(`the contract refuses ${label}`, () => { + assert.throws(() => check(observation(change)), message); + }); +}