use crate::identifiers::{InputKind, KindId, PolicyVersion};
use crate::input::Input;
use crate::policy::{
ApplyMode, ConsumePoint, DrainPolicy, PolicyDecision, QueueMode, RoutingDisposition, WakeMode,
};
pub const DEFAULT_POLICY_VERSION: PolicyVersion = PolicyVersion(1);
#[allow(clippy::too_many_arguments)]
fn pd(
apply_mode: ApplyMode,
wake_mode: WakeMode,
queue_mode: QueueMode,
consume_point: ConsumePoint,
drain_policy: DrainPolicy,
routing_disposition: RoutingDisposition,
record_transcript: bool,
) -> PolicyDecision {
PolicyDecision {
apply_mode,
wake_mode,
queue_mode,
consume_point,
drain_policy,
routing_disposition,
record_transcript,
emit_operator_content: record_transcript,
policy_version: DEFAULT_POLICY_VERSION,
}
}
pub struct DefaultPolicyTable;
impl DefaultPolicyTable {
pub fn resolve(input: &Input, runtime_idle: bool) -> PolicyDecision {
let kind = input.kind();
let is_response_progress = matches!(kind, InputKind::PeerResponseProgress);
if matches!(kind, InputKind::PeerResponseTerminal)
&& let Some(mode) = input.handling_mode()
{
let (wake_mode, drain_policy, routing_disposition) = match mode {
meerkat_core::types::HandlingMode::Queue => (
WakeMode::WakeIfIdle,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
),
meerkat_core::types::HandlingMode::Steer => (
if runtime_idle {
WakeMode::WakeIfIdle
} else {
WakeMode::InterruptYielding
},
DrainPolicy::SteerBatch,
RoutingDisposition::Steer,
),
};
return pd(
ApplyMode::StageRunStart,
wake_mode,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
drain_policy,
routing_disposition,
true,
);
}
if let Input::Continuation(continuation) = input
&& continuation
.flow_tool_overlay
.as_ref()
.is_some_and(|overlay| !overlay.dispatch_context.is_empty())
&& continuation.reason != "workgraph_attention"
{
return pd(
ApplyMode::StageRunStart,
if runtime_idle {
WakeMode::WakeIfIdle
} else {
WakeMode::InterruptYielding
},
QueueMode::Supersede,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Steer,
false,
);
}
if !is_response_progress && let Some(mode) = input.handling_mode() {
return match mode {
meerkat_core::types::HandlingMode::Queue => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
if matches!(input, Input::Continuation(_)) {
QueueMode::Supersede
} else {
QueueMode::Fifo
},
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
!matches!(input, Input::Continuation(_)),
),
meerkat_core::types::HandlingMode::Steer => pd(
if runtime_idle && matches!(input, Input::Peer(_)) {
ApplyMode::StageRunStart
} else {
ApplyMode::StageRunBoundary
},
if runtime_idle {
WakeMode::WakeIfIdle
} else {
WakeMode::InterruptYielding
},
if matches!(input, Input::Continuation(_)) {
QueueMode::Supersede
} else {
QueueMode::Fifo
},
ConsumePoint::OnRunComplete,
DrainPolicy::SteerBatch,
RoutingDisposition::Steer,
!matches!(input, Input::Continuation(_)),
),
};
}
Self::resolve_by_kind(KindId::new(kind), runtime_idle)
}
pub fn resolve_by_kind(kind: KindId, runtime_idle: bool) -> PolicyDecision {
match (kind.kind(), runtime_idle) {
(InputKind::Prompt, true) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::Prompt, false) => pd(
ApplyMode::StageRunStart,
WakeMode::None,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::PeerMessage, true) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::PeerMessage, false) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::PeerRequest, true) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::PeerRequest, false) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::PeerResponseProgress, _) => pd(
ApplyMode::StageRunBoundary,
WakeMode::None,
QueueMode::Coalesce,
ConsumePoint::OnRunComplete,
DrainPolicy::SteerBatch,
RoutingDisposition::Steer,
true,
),
(InputKind::PeerResponseTerminal, _) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::FlowStep, true) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::FlowStep, false) => pd(
ApplyMode::StageRunStart,
WakeMode::None,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::ExternalEvent, true) => pd(
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::ExternalEvent, false) => pd(
ApplyMode::StageRunStart,
WakeMode::None,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
DrainPolicy::QueueNextTurn,
RoutingDisposition::Queue,
true,
),
(InputKind::Continuation, true) => pd(
ApplyMode::StageRunBoundary,
WakeMode::WakeIfIdle,
QueueMode::Supersede,
ConsumePoint::OnRunComplete,
DrainPolicy::SteerBatch,
RoutingDisposition::Steer,
false,
),
(InputKind::Continuation, false) => pd(
ApplyMode::StageRunBoundary,
WakeMode::InterruptYielding,
QueueMode::Supersede,
ConsumePoint::OnRunComplete,
DrainPolicy::SteerBatch,
RoutingDisposition::Steer,
false,
),
(InputKind::Operation, _) => pd(
ApplyMode::Ignore,
WakeMode::None,
QueueMode::Priority,
ConsumePoint::OnAccept,
DrainPolicy::Ignore,
RoutingDisposition::Drop,
false,
),
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
fn assert_cell(
kind: InputKind,
idle: bool,
expected_apply: ApplyMode,
expected_wake: WakeMode,
expected_queue: QueueMode,
expected_consume: ConsumePoint,
expected_transcript: bool,
) {
let decision = DefaultPolicyTable::resolve_by_kind(KindId::new(kind), idle);
assert_eq!(
decision.apply_mode, expected_apply,
"kind={kind:?}, idle={idle}: apply_mode"
);
assert_eq!(
decision.wake_mode, expected_wake,
"kind={kind:?}, idle={idle}: wake_mode"
);
assert_eq!(
decision.queue_mode, expected_queue,
"kind={kind:?}, idle={idle}: queue_mode"
);
assert_eq!(
decision.consume_point, expected_consume,
"kind={kind:?}, idle={idle}: consume_point"
);
assert_eq!(
decision.record_transcript, expected_transcript,
"kind={kind:?}, idle={idle}: record_transcript"
);
}
#[test]
fn prompt_idle() {
assert_cell(
InputKind::Prompt,
true,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn prompt_running() {
assert_cell(
InputKind::Prompt,
false,
ApplyMode::StageRunStart,
WakeMode::None,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_message_idle() {
assert_cell(
InputKind::PeerMessage,
true,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_message_running() {
assert_cell(
InputKind::PeerMessage,
false,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_request_idle() {
assert_cell(
InputKind::PeerRequest,
true,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_request_running() {
assert_cell(
InputKind::PeerRequest,
false,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_response_progress_idle() {
assert_cell(
InputKind::PeerResponseProgress,
true,
ApplyMode::StageRunBoundary,
WakeMode::None,
QueueMode::Coalesce,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_response_progress_running() {
assert_cell(
InputKind::PeerResponseProgress,
false,
ApplyMode::StageRunBoundary,
WakeMode::None,
QueueMode::Coalesce,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_response_terminal_idle() {
assert_cell(
InputKind::PeerResponseTerminal,
true,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn peer_response_terminal_running() {
assert_cell(
InputKind::PeerResponseTerminal,
false,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn flow_step_idle() {
assert_cell(
InputKind::FlowStep,
true,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn flow_step_running() {
assert_cell(
InputKind::FlowStep,
false,
ApplyMode::StageRunStart,
WakeMode::None,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn external_event_idle() {
assert_cell(
InputKind::ExternalEvent,
true,
ApplyMode::StageRunStart,
WakeMode::WakeIfIdle,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn external_event_running() {
assert_cell(
InputKind::ExternalEvent,
false,
ApplyMode::StageRunStart,
WakeMode::None,
QueueMode::Fifo,
ConsumePoint::OnRunComplete,
true,
);
}
#[test]
fn continuation_idle() {
assert_cell(
InputKind::Continuation,
true,
ApplyMode::StageRunBoundary,
WakeMode::WakeIfIdle,
QueueMode::Supersede,
ConsumePoint::OnRunComplete,
false,
);
}
#[test]
fn continuation_running() {
assert_cell(
InputKind::Continuation,
false,
ApplyMode::StageRunBoundary,
WakeMode::InterruptYielding,
QueueMode::Supersede,
ConsumePoint::OnRunComplete,
false,
);
}
#[test]
fn operation_idle() {
assert_cell(
InputKind::Operation,
true,
ApplyMode::Ignore,
WakeMode::None,
QueueMode::Priority,
ConsumePoint::OnAccept,
false,
);
}
#[test]
fn operation_running() {
assert_cell(
InputKind::Operation,
false,
ApplyMode::Ignore,
WakeMode::None,
QueueMode::Priority,
ConsumePoint::OnAccept,
false,
);
}
#[test]
fn resolve_via_input_object() {
use crate::input::*;
use chrono::Utc;
use meerkat_core::lifecycle::InputId;
let header = InputHeader {
id: InputId::new(),
timestamp: Utc::now(),
source: InputOrigin::Operator,
durability: InputDurability::Durable,
visibility: InputVisibility::default(),
idempotency_key: None,
supersession_key: None,
correlation_id: None,
};
let input = Input::Prompt(PromptInput {
header,
text: "hello".into(),
blocks: None,
typed_turn_appends: Vec::new(),
turn_metadata: None,
});
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
}
#[test]
fn explicit_steer_metadata_maps_to_checkpoint_policy() {
use crate::input::*;
use chrono::Utc;
use meerkat_core::lifecycle::InputId;
use meerkat_core::lifecycle::run_primitive::RuntimeTurnMetadata;
let input = Input::Prompt(PromptInput {
header: InputHeader {
id: InputId::new(),
timestamp: Utc::now(),
source: InputOrigin::Operator,
durability: InputDurability::Durable,
visibility: InputVisibility::default(),
idempotency_key: None,
supersession_key: None,
correlation_id: None,
},
text: "hello".into(),
blocks: None,
typed_turn_appends: Vec::new(),
turn_metadata: Some(RuntimeTurnMetadata {
handling_mode: Some(meerkat_core::types::HandlingMode::Steer),
..Default::default()
}),
});
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
assert_eq!(decision.drain_policy, DrainPolicy::SteerBatch);
assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
}
#[test]
fn peer_message_running_wakes_after_current_turn() {
let decision =
DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), false);
assert_eq!(
decision.wake_mode,
WakeMode::WakeIfIdle,
"peer_message while running must wake the runtime after the current turn"
);
}
#[test]
fn peer_request_running_wakes_after_current_turn() {
let decision =
DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerRequest), false);
assert_eq!(
decision.wake_mode,
WakeMode::WakeIfIdle,
"peer_request while running must wake the runtime after the current turn"
);
}
#[test]
fn peer_message_idle_still_wakes() {
let decision =
DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), true);
assert_eq!(
decision.wake_mode,
WakeMode::WakeIfIdle,
"peer_message while idle must use WakeIfIdle"
);
}
#[test]
fn peer_request_idle_still_wakes() {
let decision =
DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerRequest), true);
assert_eq!(
decision.wake_mode,
WakeMode::WakeIfIdle,
"peer_request while idle must use WakeIfIdle"
);
}
use crate::input::{
InputDurability, InputHeader, InputOrigin, InputVisibility, PeerConvention, PeerInput,
};
use chrono::Utc;
use meerkat_core::lifecycle::InputId;
use meerkat_core::types::HandlingMode;
fn make_peer_input(
convention: Option<PeerConvention>,
handling_mode: Option<HandlingMode>,
) -> Input {
Input::Peer(PeerInput {
header: InputHeader {
id: InputId::new(),
timestamp: Utc::now(),
source: InputOrigin::Peer {
peer_id: "p".into(),
display_identity: None,
runtime_id: None,
},
durability: InputDurability::Durable,
visibility: InputVisibility::default(),
idempotency_key: None,
supersession_key: None,
correlation_id: None,
},
convention,
body: "test".into(),
payload: None,
blocks: None,
handling_mode,
})
}
#[test]
fn peer_message_with_explicit_queue_resolves_queue_semantics() {
let input = make_peer_input(Some(PeerConvention::Message), Some(HandlingMode::Queue));
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
let running_decision = DefaultPolicyTable::resolve(&input, false);
assert_eq!(
running_decision.routing_disposition,
RoutingDisposition::Queue
);
assert_eq!(
running_decision.wake_mode,
WakeMode::WakeIfIdle,
"explicit queue means next boundary and may wake the loop once idle, but must not interrupt-yield while the target is running"
);
}
#[test]
fn peer_message_with_explicit_steer_resolves_steer_semantics() {
let input = make_peer_input(Some(PeerConvention::Message), Some(HandlingMode::Steer));
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
assert_eq!(
decision.apply_mode,
ApplyMode::StageRunStart,
"idle steer has no active run boundary; it must start a turn"
);
let running_decision = DefaultPolicyTable::resolve(&input, false);
assert_eq!(
running_decision.routing_disposition,
RoutingDisposition::Steer
);
assert_eq!(running_decision.apply_mode, ApplyMode::StageRunBoundary);
}
#[test]
fn peer_request_with_explicit_steer_resolves_steer_semantics() {
let input = make_peer_input(
Some(PeerConvention::Request {
request_id: "r".into(),
intent: "i".into(),
}),
Some(HandlingMode::Steer),
);
let idle_decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(idle_decision.routing_disposition, RoutingDisposition::Steer);
assert_eq!(
idle_decision.apply_mode,
ApplyMode::StageRunStart,
"idle peer requests with explicit steer must not strand on a missing boundary"
);
let running_decision = DefaultPolicyTable::resolve(&input, false);
assert_eq!(
running_decision.routing_disposition,
RoutingDisposition::Steer
);
assert_eq!(running_decision.apply_mode, ApplyMode::StageRunBoundary);
}
#[test]
fn peer_no_convention_with_explicit_steer_resolves_steer_semantics() {
let input = make_peer_input(None, Some(HandlingMode::Steer));
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
}
#[test]
fn peer_message_without_override_preserves_kind_default() {
let input = make_peer_input(Some(PeerConvention::Message), None);
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
}
#[test]
fn response_progress_with_handling_mode_falls_through_to_kind_default() {
let input = make_peer_input(
Some(PeerConvention::ResponseProgress {
request_id: "r".into(),
phase: crate::input::ResponseProgressPhase::InProgress,
}),
Some(HandlingMode::Steer),
);
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.queue_mode, QueueMode::Coalesce);
assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
assert_eq!(decision.wake_mode, WakeMode::None);
}
#[test]
fn response_terminal_with_steer_gets_steer_semantics() {
let input = make_peer_input(
Some(PeerConvention::ResponseTerminal {
request_id: "r".into(),
status: crate::input::ResponseTerminalStatus::Completed,
}),
Some(HandlingMode::Steer),
);
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
assert_eq!(
decision.apply_mode,
ApplyMode::StageRunStart,
"terminal peer-response apply intent owns the context+reaction boundary; steer only changes urgency/lane"
);
assert_eq!(decision.drain_policy, DrainPolicy::SteerBatch);
assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
assert!(decision.record_transcript);
}
#[test]
fn response_terminal_with_queue_handling_mode_gets_queue_semantics() {
let input = make_peer_input(
Some(PeerConvention::ResponseTerminal {
request_id: "r".into(),
status: crate::input::ResponseTerminalStatus::Completed,
}),
Some(HandlingMode::Queue),
);
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
let running_decision = DefaultPolicyTable::resolve(&input, false);
assert_eq!(
running_decision.routing_disposition,
RoutingDisposition::Queue
);
assert_eq!(running_decision.apply_mode, ApplyMode::StageRunStart);
assert_eq!(running_decision.wake_mode, WakeMode::WakeIfIdle);
}
#[test]
fn response_terminal_without_handling_mode_keeps_kind_default() {
let input = make_peer_input(
Some(PeerConvention::ResponseTerminal {
request_id: "r".into(),
status: crate::input::ResponseTerminalStatus::Completed,
}),
None,
);
let decision = DefaultPolicyTable::resolve(&input, true);
assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
}
#[test]
fn workgraph_attention_continuation_with_overlay_remains_ordinary_queue_work() {
use crate::identifiers::SupersessionKey;
use crate::input::{ContinuationInput, Input};
use meerkat_core::service::TurnToolOverlay;
use std::collections::BTreeMap;
let input = Input::Continuation(ContinuationInput {
header: InputHeader {
id: InputId::new(),
timestamp: Utc::now(),
source: InputOrigin::System,
durability: InputDurability::Durable,
visibility: InputVisibility {
transcript_eligible: false,
operator_eligible: false,
},
idempotency_key: Some(crate::IdempotencyKey::new(
"workgraph_attention:realm:namespace:binding:1:1:digest",
)),
supersession_key: Some(SupersessionKey::new(
"workgraph_attention:realm:namespace:binding",
)),
correlation_id: None,
},
reason: "workgraph_attention".to_string(),
handling_mode: HandlingMode::Queue,
request_id: Some("binding".to_string()),
flow_tool_overlay: Some(TurnToolOverlay {
allowed_tools: Some(vec!["workgraph_add_evidence".to_string()]),
blocked_tools: None,
dispatch_context: BTreeMap::from([(
"workgraph.attention_projection".to_string(),
serde_json::json!({"binding_id": "binding"}),
)]),
}),
context_append: None,
turn_append: None,
});
let running_decision = DefaultPolicyTable::resolve(&input, false);
assert_eq!(
running_decision.routing_disposition,
RoutingDisposition::Queue
);
assert_eq!(running_decision.apply_mode, ApplyMode::StageRunStart);
assert_eq!(running_decision.wake_mode, WakeMode::WakeIfIdle);
assert_eq!(running_decision.drain_policy, DrainPolicy::QueueNextTurn);
}
}