1use crate::identifiers::{InputKind, KindId, PolicyVersion};
6use crate::input::Input;
7use crate::policy::{
8 ApplyMode, ConsumePoint, DrainPolicy, PolicyDecision, QueueMode, RoutingDisposition, WakeMode,
9};
10
11pub const DEFAULT_POLICY_VERSION: PolicyVersion = PolicyVersion(1);
13
14#[allow(clippy::too_many_arguments)]
16fn pd(
17 apply_mode: ApplyMode,
18 wake_mode: WakeMode,
19 queue_mode: QueueMode,
20 consume_point: ConsumePoint,
21 drain_policy: DrainPolicy,
22 routing_disposition: RoutingDisposition,
23 record_transcript: bool,
24) -> PolicyDecision {
25 PolicyDecision {
26 apply_mode,
27 wake_mode,
28 queue_mode,
29 consume_point,
30 drain_policy,
31 routing_disposition,
32 record_transcript,
33 emit_operator_content: record_transcript,
34 policy_version: DEFAULT_POLICY_VERSION,
35 }
36}
37
38pub struct DefaultPolicyTable;
40
41impl DefaultPolicyTable {
42 pub fn resolve(input: &Input, runtime_idle: bool) -> PolicyDecision {
51 let kind = input.kind();
52 let is_response_progress = matches!(kind, InputKind::PeerResponseProgress);
57 if matches!(kind, InputKind::PeerResponseTerminal)
58 && let Some(mode) = input.handling_mode()
59 {
60 let (wake_mode, drain_policy, routing_disposition) = match mode {
61 meerkat_core::types::HandlingMode::Queue => (
62 WakeMode::WakeIfIdle,
63 DrainPolicy::QueueNextTurn,
64 RoutingDisposition::Queue,
65 ),
66 meerkat_core::types::HandlingMode::Steer => (
67 if runtime_idle {
68 WakeMode::WakeIfIdle
69 } else {
70 WakeMode::InterruptYielding
71 },
72 DrainPolicy::SteerBatch,
73 RoutingDisposition::Steer,
74 ),
75 };
76 return pd(
77 ApplyMode::StageRunStart,
78 wake_mode,
79 QueueMode::Fifo,
80 ConsumePoint::OnRunComplete,
81 drain_policy,
82 routing_disposition,
83 true,
84 );
85 }
86 if !is_response_progress && let Some(mode) = input.handling_mode() {
87 return match mode {
88 meerkat_core::types::HandlingMode::Queue => pd(
89 ApplyMode::StageRunStart,
90 if runtime_idle {
91 WakeMode::WakeIfIdle
92 } else {
93 WakeMode::None
94 },
95 QueueMode::Fifo,
96 ConsumePoint::OnRunComplete,
97 DrainPolicy::QueueNextTurn,
98 RoutingDisposition::Queue,
99 !matches!(input, Input::Continuation(_)),
100 ),
101 meerkat_core::types::HandlingMode::Steer => pd(
102 ApplyMode::StageRunBoundary,
103 if runtime_idle {
104 WakeMode::WakeIfIdle
105 } else {
106 WakeMode::InterruptYielding
107 },
108 QueueMode::Fifo,
109 ConsumePoint::OnRunComplete,
110 DrainPolicy::SteerBatch,
111 RoutingDisposition::Steer,
112 !matches!(input, Input::Continuation(_)),
113 ),
114 };
115 }
116
117 Self::resolve_by_kind(KindId::new(kind), runtime_idle)
118 }
119
120 pub fn resolve_by_kind(kind: KindId, runtime_idle: bool) -> PolicyDecision {
122 match (kind.kind(), runtime_idle) {
123 (InputKind::Prompt, true) => pd(
125 ApplyMode::StageRunStart,
126 WakeMode::WakeIfIdle,
127 QueueMode::Fifo,
128 ConsumePoint::OnRunComplete,
129 DrainPolicy::QueueNextTurn,
130 RoutingDisposition::Queue,
131 true,
132 ),
133 (InputKind::Prompt, false) => pd(
134 ApplyMode::StageRunStart,
135 WakeMode::None,
136 QueueMode::Fifo,
137 ConsumePoint::OnRunComplete,
138 DrainPolicy::QueueNextTurn,
139 RoutingDisposition::Queue,
140 true,
141 ),
142
143 (InputKind::PeerMessage, true) => pd(
146 ApplyMode::StageRunStart,
147 WakeMode::WakeIfIdle,
148 QueueMode::Fifo,
149 ConsumePoint::OnRunComplete,
150 DrainPolicy::QueueNextTurn,
151 RoutingDisposition::Queue,
152 true,
153 ),
154 (InputKind::PeerMessage, false) => pd(
155 ApplyMode::StageRunStart,
156 WakeMode::InterruptYielding,
157 QueueMode::Fifo,
158 ConsumePoint::OnRunComplete,
159 DrainPolicy::QueueNextTurn,
160 RoutingDisposition::Queue,
161 true,
162 ),
163
164 (InputKind::PeerRequest, true) => pd(
166 ApplyMode::StageRunStart,
167 WakeMode::WakeIfIdle,
168 QueueMode::Fifo,
169 ConsumePoint::OnRunComplete,
170 DrainPolicy::QueueNextTurn,
171 RoutingDisposition::Queue,
172 true,
173 ),
174 (InputKind::PeerRequest, false) => pd(
175 ApplyMode::StageRunStart,
176 WakeMode::InterruptYielding,
177 QueueMode::Fifo,
178 ConsumePoint::OnRunComplete,
179 DrainPolicy::QueueNextTurn,
180 RoutingDisposition::Queue,
181 true,
182 ),
183
184 (InputKind::PeerResponseProgress, _) => pd(
186 ApplyMode::StageRunBoundary,
187 WakeMode::None,
188 QueueMode::Coalesce,
189 ConsumePoint::OnRunComplete,
190 DrainPolicy::SteerBatch,
191 RoutingDisposition::Steer,
192 true,
193 ),
194
195 (InputKind::PeerResponseTerminal, _) => pd(
217 ApplyMode::StageRunStart,
218 WakeMode::WakeIfIdle,
219 QueueMode::Fifo,
220 ConsumePoint::OnRunComplete,
221 DrainPolicy::QueueNextTurn,
222 RoutingDisposition::Queue,
223 true,
224 ),
225
226 (InputKind::FlowStep, true) => pd(
228 ApplyMode::StageRunStart,
229 WakeMode::WakeIfIdle,
230 QueueMode::Fifo,
231 ConsumePoint::OnRunComplete,
232 DrainPolicy::QueueNextTurn,
233 RoutingDisposition::Queue,
234 true,
235 ),
236 (InputKind::FlowStep, false) => pd(
237 ApplyMode::StageRunStart,
238 WakeMode::None,
239 QueueMode::Fifo,
240 ConsumePoint::OnRunComplete,
241 DrainPolicy::QueueNextTurn,
242 RoutingDisposition::Queue,
243 true,
244 ),
245
246 (InputKind::ExternalEvent, true) => pd(
248 ApplyMode::StageRunStart,
249 WakeMode::WakeIfIdle,
250 QueueMode::Fifo,
251 ConsumePoint::OnRunComplete,
252 DrainPolicy::QueueNextTurn,
253 RoutingDisposition::Queue,
254 true,
255 ),
256 (InputKind::ExternalEvent, false) => pd(
257 ApplyMode::StageRunStart,
258 WakeMode::None,
259 QueueMode::Fifo,
260 ConsumePoint::OnRunComplete,
261 DrainPolicy::QueueNextTurn,
262 RoutingDisposition::Queue,
263 true,
264 ),
265
266 (InputKind::Continuation, true) => pd(
268 ApplyMode::StageRunBoundary,
269 WakeMode::WakeIfIdle,
270 QueueMode::Fifo,
271 ConsumePoint::OnRunComplete,
272 DrainPolicy::SteerBatch,
273 RoutingDisposition::Steer,
274 false,
275 ),
276 (InputKind::Continuation, false) => pd(
277 ApplyMode::StageRunBoundary,
278 WakeMode::InterruptYielding,
279 QueueMode::Fifo,
280 ConsumePoint::OnRunComplete,
281 DrainPolicy::SteerBatch,
282 RoutingDisposition::Steer,
283 false,
284 ),
285
286 (InputKind::Operation, _) => pd(
289 ApplyMode::Ignore,
290 WakeMode::None,
291 QueueMode::Priority,
292 ConsumePoint::OnAccept,
293 DrainPolicy::Ignore,
294 RoutingDisposition::Drop,
295 false,
296 ),
297 }
298 }
299}
300
301#[cfg(test)]
302#[allow(clippy::unwrap_used)]
303mod tests {
304 use super::*;
305
306 fn assert_cell(
307 kind: InputKind,
308 idle: bool,
309 expected_apply: ApplyMode,
310 expected_wake: WakeMode,
311 expected_queue: QueueMode,
312 expected_consume: ConsumePoint,
313 expected_transcript: bool,
314 ) {
315 let decision = DefaultPolicyTable::resolve_by_kind(KindId::new(kind), idle);
316 assert_eq!(
317 decision.apply_mode, expected_apply,
318 "kind={kind:?}, idle={idle}: apply_mode"
319 );
320 assert_eq!(
321 decision.wake_mode, expected_wake,
322 "kind={kind:?}, idle={idle}: wake_mode"
323 );
324 assert_eq!(
325 decision.queue_mode, expected_queue,
326 "kind={kind:?}, idle={idle}: queue_mode"
327 );
328 assert_eq!(
329 decision.consume_point, expected_consume,
330 "kind={kind:?}, idle={idle}: consume_point"
331 );
332 assert_eq!(
333 decision.record_transcript, expected_transcript,
334 "kind={kind:?}, idle={idle}: record_transcript"
335 );
336 }
337
338 #[test]
339 fn prompt_idle() {
340 assert_cell(
341 InputKind::Prompt,
342 true,
343 ApplyMode::StageRunStart,
344 WakeMode::WakeIfIdle,
345 QueueMode::Fifo,
346 ConsumePoint::OnRunComplete,
347 true,
348 );
349 }
350 #[test]
351 fn prompt_running() {
352 assert_cell(
353 InputKind::Prompt,
354 false,
355 ApplyMode::StageRunStart,
356 WakeMode::None,
357 QueueMode::Fifo,
358 ConsumePoint::OnRunComplete,
359 true,
360 );
361 }
362 #[test]
363 fn peer_message_idle() {
364 assert_cell(
365 InputKind::PeerMessage,
366 true,
367 ApplyMode::StageRunStart,
368 WakeMode::WakeIfIdle,
369 QueueMode::Fifo,
370 ConsumePoint::OnRunComplete,
371 true,
372 );
373 }
374 #[test]
375 fn peer_message_running() {
376 assert_cell(
377 InputKind::PeerMessage,
378 false,
379 ApplyMode::StageRunStart,
380 WakeMode::InterruptYielding,
381 QueueMode::Fifo,
382 ConsumePoint::OnRunComplete,
383 true,
384 );
385 }
386 #[test]
387 fn peer_request_idle() {
388 assert_cell(
389 InputKind::PeerRequest,
390 true,
391 ApplyMode::StageRunStart,
392 WakeMode::WakeIfIdle,
393 QueueMode::Fifo,
394 ConsumePoint::OnRunComplete,
395 true,
396 );
397 }
398 #[test]
399 fn peer_request_running() {
400 assert_cell(
401 InputKind::PeerRequest,
402 false,
403 ApplyMode::StageRunStart,
404 WakeMode::InterruptYielding,
405 QueueMode::Fifo,
406 ConsumePoint::OnRunComplete,
407 true,
408 );
409 }
410 #[test]
411 fn peer_response_progress_idle() {
412 assert_cell(
413 InputKind::PeerResponseProgress,
414 true,
415 ApplyMode::StageRunBoundary,
416 WakeMode::None,
417 QueueMode::Coalesce,
418 ConsumePoint::OnRunComplete,
419 true,
420 );
421 }
422 #[test]
423 fn peer_response_progress_running() {
424 assert_cell(
425 InputKind::PeerResponseProgress,
426 false,
427 ApplyMode::StageRunBoundary,
428 WakeMode::None,
429 QueueMode::Coalesce,
430 ConsumePoint::OnRunComplete,
431 true,
432 );
433 }
434 #[test]
435 fn peer_response_terminal_idle() {
436 assert_cell(
437 InputKind::PeerResponseTerminal,
438 true,
439 ApplyMode::StageRunStart,
440 WakeMode::WakeIfIdle,
441 QueueMode::Fifo,
442 ConsumePoint::OnRunComplete,
443 true,
444 );
445 }
446 #[test]
447 fn peer_response_terminal_running() {
448 assert_cell(
449 InputKind::PeerResponseTerminal,
450 false,
451 ApplyMode::StageRunStart,
452 WakeMode::WakeIfIdle,
453 QueueMode::Fifo,
454 ConsumePoint::OnRunComplete,
455 true,
456 );
457 }
458 #[test]
459 fn flow_step_idle() {
460 assert_cell(
461 InputKind::FlowStep,
462 true,
463 ApplyMode::StageRunStart,
464 WakeMode::WakeIfIdle,
465 QueueMode::Fifo,
466 ConsumePoint::OnRunComplete,
467 true,
468 );
469 }
470 #[test]
471 fn flow_step_running() {
472 assert_cell(
473 InputKind::FlowStep,
474 false,
475 ApplyMode::StageRunStart,
476 WakeMode::None,
477 QueueMode::Fifo,
478 ConsumePoint::OnRunComplete,
479 true,
480 );
481 }
482 #[test]
483 fn external_event_idle() {
484 assert_cell(
485 InputKind::ExternalEvent,
486 true,
487 ApplyMode::StageRunStart,
488 WakeMode::WakeIfIdle,
489 QueueMode::Fifo,
490 ConsumePoint::OnRunComplete,
491 true,
492 );
493 }
494 #[test]
495 fn external_event_running() {
496 assert_cell(
497 InputKind::ExternalEvent,
498 false,
499 ApplyMode::StageRunStart,
500 WakeMode::None,
501 QueueMode::Fifo,
502 ConsumePoint::OnRunComplete,
503 true,
504 );
505 }
506 #[test]
507 fn continuation_idle() {
508 assert_cell(
509 InputKind::Continuation,
510 true,
511 ApplyMode::StageRunBoundary,
512 WakeMode::WakeIfIdle,
513 QueueMode::Fifo,
514 ConsumePoint::OnRunComplete,
515 false,
516 );
517 }
518 #[test]
519 fn continuation_running() {
520 assert_cell(
521 InputKind::Continuation,
522 false,
523 ApplyMode::StageRunBoundary,
524 WakeMode::InterruptYielding,
525 QueueMode::Fifo,
526 ConsumePoint::OnRunComplete,
527 false,
528 );
529 }
530 #[test]
531 fn operation_idle() {
532 assert_cell(
533 InputKind::Operation,
534 true,
535 ApplyMode::Ignore,
536 WakeMode::None,
537 QueueMode::Priority,
538 ConsumePoint::OnAccept,
539 false,
540 );
541 }
542 #[test]
543 fn operation_running() {
544 assert_cell(
545 InputKind::Operation,
546 false,
547 ApplyMode::Ignore,
548 WakeMode::None,
549 QueueMode::Priority,
550 ConsumePoint::OnAccept,
551 false,
552 );
553 }
554
555 #[test]
556 fn resolve_via_input_object() {
557 use crate::input::*;
558 use chrono::Utc;
559 use meerkat_core::lifecycle::InputId;
560
561 let header = InputHeader {
562 id: InputId::new(),
563 timestamp: Utc::now(),
564 source: InputOrigin::Operator,
565 durability: InputDurability::Durable,
566 visibility: InputVisibility::default(),
567 idempotency_key: None,
568 supersession_key: None,
569 correlation_id: None,
570 };
571 let input = Input::Prompt(PromptInput {
572 header,
573 text: "hello".into(),
574 blocks: None,
575 typed_turn_appends: Vec::new(),
576 turn_metadata: None,
577 });
578 let decision = DefaultPolicyTable::resolve(&input, true);
579 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
580 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
581 }
582
583 #[test]
584 fn explicit_steer_metadata_maps_to_checkpoint_policy() {
585 use crate::input::*;
586 use chrono::Utc;
587 use meerkat_core::lifecycle::InputId;
588 use meerkat_core::lifecycle::run_primitive::RuntimeTurnMetadata;
589
590 let input = Input::Prompt(PromptInput {
591 header: InputHeader {
592 id: InputId::new(),
593 timestamp: Utc::now(),
594 source: InputOrigin::Operator,
595 durability: InputDurability::Durable,
596 visibility: InputVisibility::default(),
597 idempotency_key: None,
598 supersession_key: None,
599 correlation_id: None,
600 },
601 text: "hello".into(),
602 blocks: None,
603 typed_turn_appends: Vec::new(),
604 turn_metadata: Some(RuntimeTurnMetadata {
605 handling_mode: Some(meerkat_core::types::HandlingMode::Steer),
606 ..Default::default()
607 }),
608 });
609 let decision = DefaultPolicyTable::resolve(&input, true);
610 assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
611 assert_eq!(decision.drain_policy, DrainPolicy::SteerBatch);
612 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
613 }
614
615 #[test]
616 fn peer_message_running_stays_queued_without_wake() {
617 let decision =
618 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), false);
619 assert_eq!(
620 decision.wake_mode,
621 WakeMode::InterruptYielding,
622 "peer_message while running must interrupt cooperative yielding"
623 );
624 }
625
626 #[test]
627 fn peer_request_running_interrupts_yielding() {
628 let decision =
629 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerRequest), false);
630 assert_eq!(
631 decision.wake_mode,
632 WakeMode::InterruptYielding,
633 "peer_request while running must interrupt cooperative yielding"
634 );
635 }
636
637 #[test]
638 fn peer_message_idle_still_wakes() {
639 let decision =
641 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), true);
642 assert_eq!(
643 decision.wake_mode,
644 WakeMode::WakeIfIdle,
645 "peer_message while idle must use WakeIfIdle"
646 );
647 }
648
649 #[test]
650 fn peer_request_idle_still_wakes() {
651 let decision =
653 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerRequest), true);
654 assert_eq!(
655 decision.wake_mode,
656 WakeMode::WakeIfIdle,
657 "peer_request while idle must use WakeIfIdle"
658 );
659 }
660
661 use crate::input::{
666 InputDurability, InputHeader, InputOrigin, InputVisibility, PeerConvention, PeerInput,
667 };
668 use chrono::Utc;
669 use meerkat_core::lifecycle::InputId;
670 use meerkat_core::types::HandlingMode;
671
672 fn make_peer_input(
673 convention: Option<PeerConvention>,
674 handling_mode: Option<HandlingMode>,
675 ) -> Input {
676 Input::Peer(PeerInput {
677 header: InputHeader {
678 id: InputId::new(),
679 timestamp: Utc::now(),
680 source: InputOrigin::Peer {
681 peer_id: "p".into(),
682 display_identity: None,
683 runtime_id: None,
684 },
685 durability: InputDurability::Durable,
686 visibility: InputVisibility::default(),
687 idempotency_key: None,
688 supersession_key: None,
689 correlation_id: None,
690 },
691 convention,
692 body: "test".into(),
693 payload: None,
694 blocks: None,
695 handling_mode,
696 })
697 }
698
699 #[test]
700 fn peer_message_with_explicit_queue_resolves_queue_semantics() {
701 let input = make_peer_input(Some(PeerConvention::Message), Some(HandlingMode::Queue));
702 let decision = DefaultPolicyTable::resolve(&input, true);
703 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
704 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
705
706 let running_decision = DefaultPolicyTable::resolve(&input, false);
707 assert_eq!(
708 running_decision.routing_disposition,
709 RoutingDisposition::Queue
710 );
711 assert_eq!(
712 running_decision.wake_mode,
713 WakeMode::None,
714 "explicit queue means next boundary, not interrupt-yielding, while the target is running"
715 );
716 }
717
718 #[test]
719 fn peer_message_with_explicit_steer_resolves_steer_semantics() {
720 let input = make_peer_input(Some(PeerConvention::Message), Some(HandlingMode::Steer));
721 let decision = DefaultPolicyTable::resolve(&input, true);
722 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
723 assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
724 }
725
726 #[test]
727 fn peer_request_with_explicit_steer_resolves_steer_semantics() {
728 let input = make_peer_input(
729 Some(PeerConvention::Request {
730 request_id: "r".into(),
731 intent: "i".into(),
732 }),
733 Some(HandlingMode::Steer),
734 );
735 let decision = DefaultPolicyTable::resolve(&input, false);
736 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
737 }
738
739 #[test]
740 fn peer_no_convention_with_explicit_steer_resolves_steer_semantics() {
741 let input = make_peer_input(None, Some(HandlingMode::Steer));
742 let decision = DefaultPolicyTable::resolve(&input, true);
743 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
744 }
745
746 #[test]
747 fn peer_message_without_override_preserves_kind_default() {
748 let input = make_peer_input(Some(PeerConvention::Message), None);
749 let decision = DefaultPolicyTable::resolve(&input, true);
750 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
752 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
753 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
754 }
755
756 #[test]
761 fn response_progress_with_handling_mode_falls_through_to_kind_default() {
762 let input = make_peer_input(
765 Some(PeerConvention::ResponseProgress {
766 request_id: "r".into(),
767 phase: crate::input::ResponseProgressPhase::InProgress,
768 }),
769 Some(HandlingMode::Steer),
770 );
771 let decision = DefaultPolicyTable::resolve(&input, true);
772 assert_eq!(decision.queue_mode, QueueMode::Coalesce);
775 assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
776 assert_eq!(decision.wake_mode, WakeMode::None);
777 }
778
779 #[test]
780 fn response_terminal_with_steer_gets_steer_semantics() {
781 let input = make_peer_input(
782 Some(PeerConvention::ResponseTerminal {
783 request_id: "r".into(),
784 status: crate::input::ResponseTerminalStatus::Completed,
785 }),
786 Some(HandlingMode::Steer),
787 );
788 let decision = DefaultPolicyTable::resolve(&input, true);
789 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
790 assert_eq!(
791 decision.apply_mode,
792 ApplyMode::StageRunStart,
793 "terminal peer-response apply intent owns the context+reaction boundary; steer only changes urgency/lane"
794 );
795 assert_eq!(decision.drain_policy, DrainPolicy::SteerBatch);
796 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
797 assert!(decision.record_transcript);
798 }
799
800 #[test]
801 fn response_terminal_with_queue_handling_mode_gets_queue_semantics() {
802 let input = make_peer_input(
803 Some(PeerConvention::ResponseTerminal {
804 request_id: "r".into(),
805 status: crate::input::ResponseTerminalStatus::Completed,
806 }),
807 Some(HandlingMode::Queue),
808 );
809 let decision = DefaultPolicyTable::resolve(&input, true);
810 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
811 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
812 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
813
814 let running_decision = DefaultPolicyTable::resolve(&input, false);
815 assert_eq!(
816 running_decision.routing_disposition,
817 RoutingDisposition::Queue
818 );
819 assert_eq!(running_decision.apply_mode, ApplyMode::StageRunStart);
820 assert_eq!(running_decision.wake_mode, WakeMode::WakeIfIdle);
821 }
822
823 #[test]
824 fn response_terminal_without_handling_mode_keeps_kind_default() {
825 let input = make_peer_input(
826 Some(PeerConvention::ResponseTerminal {
827 request_id: "r".into(),
828 status: crate::input::ResponseTerminalStatus::Completed,
829 }),
830 None,
831 );
832 let decision = DefaultPolicyTable::resolve(&input, true);
833 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
840 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
841 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
842 }
843}