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(
151 ApplyMode::StageRunStart,
152 WakeMode::WakeIfIdle,
153 QueueMode::Fifo,
154 ConsumePoint::OnRunComplete,
155 DrainPolicy::QueueNextTurn,
156 RoutingDisposition::Queue,
157 true,
158 ),
159 (InputKind::PeerMessage, false) => pd(
160 ApplyMode::StageRunStart,
161 WakeMode::WakeIfIdle,
162 QueueMode::Fifo,
163 ConsumePoint::OnRunComplete,
164 DrainPolicy::QueueNextTurn,
165 RoutingDisposition::Queue,
166 true,
167 ),
168
169 (InputKind::PeerRequest, true) => pd(
171 ApplyMode::StageRunStart,
172 WakeMode::WakeIfIdle,
173 QueueMode::Fifo,
174 ConsumePoint::OnRunComplete,
175 DrainPolicy::QueueNextTurn,
176 RoutingDisposition::Queue,
177 true,
178 ),
179 (InputKind::PeerRequest, false) => pd(
180 ApplyMode::StageRunStart,
181 WakeMode::WakeIfIdle,
182 QueueMode::Fifo,
183 ConsumePoint::OnRunComplete,
184 DrainPolicy::QueueNextTurn,
185 RoutingDisposition::Queue,
186 true,
187 ),
188
189 (InputKind::PeerResponseProgress, _) => pd(
191 ApplyMode::StageRunBoundary,
192 WakeMode::None,
193 QueueMode::Coalesce,
194 ConsumePoint::OnRunComplete,
195 DrainPolicy::SteerBatch,
196 RoutingDisposition::Steer,
197 true,
198 ),
199
200 (InputKind::PeerResponseTerminal, _) => pd(
222 ApplyMode::StageRunStart,
223 WakeMode::WakeIfIdle,
224 QueueMode::Fifo,
225 ConsumePoint::OnRunComplete,
226 DrainPolicy::QueueNextTurn,
227 RoutingDisposition::Queue,
228 true,
229 ),
230
231 (InputKind::FlowStep, true) => pd(
233 ApplyMode::StageRunStart,
234 WakeMode::WakeIfIdle,
235 QueueMode::Fifo,
236 ConsumePoint::OnRunComplete,
237 DrainPolicy::QueueNextTurn,
238 RoutingDisposition::Queue,
239 true,
240 ),
241 (InputKind::FlowStep, false) => pd(
242 ApplyMode::StageRunStart,
243 WakeMode::None,
244 QueueMode::Fifo,
245 ConsumePoint::OnRunComplete,
246 DrainPolicy::QueueNextTurn,
247 RoutingDisposition::Queue,
248 true,
249 ),
250
251 (InputKind::ExternalEvent, true) => pd(
253 ApplyMode::StageRunStart,
254 WakeMode::WakeIfIdle,
255 QueueMode::Fifo,
256 ConsumePoint::OnRunComplete,
257 DrainPolicy::QueueNextTurn,
258 RoutingDisposition::Queue,
259 true,
260 ),
261 (InputKind::ExternalEvent, false) => pd(
262 ApplyMode::StageRunStart,
263 WakeMode::None,
264 QueueMode::Fifo,
265 ConsumePoint::OnRunComplete,
266 DrainPolicy::QueueNextTurn,
267 RoutingDisposition::Queue,
268 true,
269 ),
270
271 (InputKind::Continuation, true) => pd(
273 ApplyMode::StageRunBoundary,
274 WakeMode::WakeIfIdle,
275 QueueMode::Fifo,
276 ConsumePoint::OnRunComplete,
277 DrainPolicy::SteerBatch,
278 RoutingDisposition::Steer,
279 false,
280 ),
281 (InputKind::Continuation, false) => pd(
282 ApplyMode::StageRunBoundary,
283 WakeMode::InterruptYielding,
284 QueueMode::Fifo,
285 ConsumePoint::OnRunComplete,
286 DrainPolicy::SteerBatch,
287 RoutingDisposition::Steer,
288 false,
289 ),
290
291 (InputKind::Operation, _) => pd(
294 ApplyMode::Ignore,
295 WakeMode::None,
296 QueueMode::Priority,
297 ConsumePoint::OnAccept,
298 DrainPolicy::Ignore,
299 RoutingDisposition::Drop,
300 false,
301 ),
302 }
303 }
304}
305
306#[cfg(test)]
307#[allow(clippy::unwrap_used)]
308mod tests {
309 use super::*;
310
311 fn assert_cell(
312 kind: InputKind,
313 idle: bool,
314 expected_apply: ApplyMode,
315 expected_wake: WakeMode,
316 expected_queue: QueueMode,
317 expected_consume: ConsumePoint,
318 expected_transcript: bool,
319 ) {
320 let decision = DefaultPolicyTable::resolve_by_kind(KindId::new(kind), idle);
321 assert_eq!(
322 decision.apply_mode, expected_apply,
323 "kind={kind:?}, idle={idle}: apply_mode"
324 );
325 assert_eq!(
326 decision.wake_mode, expected_wake,
327 "kind={kind:?}, idle={idle}: wake_mode"
328 );
329 assert_eq!(
330 decision.queue_mode, expected_queue,
331 "kind={kind:?}, idle={idle}: queue_mode"
332 );
333 assert_eq!(
334 decision.consume_point, expected_consume,
335 "kind={kind:?}, idle={idle}: consume_point"
336 );
337 assert_eq!(
338 decision.record_transcript, expected_transcript,
339 "kind={kind:?}, idle={idle}: record_transcript"
340 );
341 }
342
343 #[test]
344 fn prompt_idle() {
345 assert_cell(
346 InputKind::Prompt,
347 true,
348 ApplyMode::StageRunStart,
349 WakeMode::WakeIfIdle,
350 QueueMode::Fifo,
351 ConsumePoint::OnRunComplete,
352 true,
353 );
354 }
355 #[test]
356 fn prompt_running() {
357 assert_cell(
358 InputKind::Prompt,
359 false,
360 ApplyMode::StageRunStart,
361 WakeMode::None,
362 QueueMode::Fifo,
363 ConsumePoint::OnRunComplete,
364 true,
365 );
366 }
367 #[test]
368 fn peer_message_idle() {
369 assert_cell(
370 InputKind::PeerMessage,
371 true,
372 ApplyMode::StageRunStart,
373 WakeMode::WakeIfIdle,
374 QueueMode::Fifo,
375 ConsumePoint::OnRunComplete,
376 true,
377 );
378 }
379 #[test]
380 fn peer_message_running() {
381 assert_cell(
382 InputKind::PeerMessage,
383 false,
384 ApplyMode::StageRunStart,
385 WakeMode::WakeIfIdle,
386 QueueMode::Fifo,
387 ConsumePoint::OnRunComplete,
388 true,
389 );
390 }
391 #[test]
392 fn peer_request_idle() {
393 assert_cell(
394 InputKind::PeerRequest,
395 true,
396 ApplyMode::StageRunStart,
397 WakeMode::WakeIfIdle,
398 QueueMode::Fifo,
399 ConsumePoint::OnRunComplete,
400 true,
401 );
402 }
403 #[test]
404 fn peer_request_running() {
405 assert_cell(
406 InputKind::PeerRequest,
407 false,
408 ApplyMode::StageRunStart,
409 WakeMode::WakeIfIdle,
410 QueueMode::Fifo,
411 ConsumePoint::OnRunComplete,
412 true,
413 );
414 }
415 #[test]
416 fn peer_response_progress_idle() {
417 assert_cell(
418 InputKind::PeerResponseProgress,
419 true,
420 ApplyMode::StageRunBoundary,
421 WakeMode::None,
422 QueueMode::Coalesce,
423 ConsumePoint::OnRunComplete,
424 true,
425 );
426 }
427 #[test]
428 fn peer_response_progress_running() {
429 assert_cell(
430 InputKind::PeerResponseProgress,
431 false,
432 ApplyMode::StageRunBoundary,
433 WakeMode::None,
434 QueueMode::Coalesce,
435 ConsumePoint::OnRunComplete,
436 true,
437 );
438 }
439 #[test]
440 fn peer_response_terminal_idle() {
441 assert_cell(
442 InputKind::PeerResponseTerminal,
443 true,
444 ApplyMode::StageRunStart,
445 WakeMode::WakeIfIdle,
446 QueueMode::Fifo,
447 ConsumePoint::OnRunComplete,
448 true,
449 );
450 }
451 #[test]
452 fn peer_response_terminal_running() {
453 assert_cell(
454 InputKind::PeerResponseTerminal,
455 false,
456 ApplyMode::StageRunStart,
457 WakeMode::WakeIfIdle,
458 QueueMode::Fifo,
459 ConsumePoint::OnRunComplete,
460 true,
461 );
462 }
463 #[test]
464 fn flow_step_idle() {
465 assert_cell(
466 InputKind::FlowStep,
467 true,
468 ApplyMode::StageRunStart,
469 WakeMode::WakeIfIdle,
470 QueueMode::Fifo,
471 ConsumePoint::OnRunComplete,
472 true,
473 );
474 }
475 #[test]
476 fn flow_step_running() {
477 assert_cell(
478 InputKind::FlowStep,
479 false,
480 ApplyMode::StageRunStart,
481 WakeMode::None,
482 QueueMode::Fifo,
483 ConsumePoint::OnRunComplete,
484 true,
485 );
486 }
487 #[test]
488 fn external_event_idle() {
489 assert_cell(
490 InputKind::ExternalEvent,
491 true,
492 ApplyMode::StageRunStart,
493 WakeMode::WakeIfIdle,
494 QueueMode::Fifo,
495 ConsumePoint::OnRunComplete,
496 true,
497 );
498 }
499 #[test]
500 fn external_event_running() {
501 assert_cell(
502 InputKind::ExternalEvent,
503 false,
504 ApplyMode::StageRunStart,
505 WakeMode::None,
506 QueueMode::Fifo,
507 ConsumePoint::OnRunComplete,
508 true,
509 );
510 }
511 #[test]
512 fn continuation_idle() {
513 assert_cell(
514 InputKind::Continuation,
515 true,
516 ApplyMode::StageRunBoundary,
517 WakeMode::WakeIfIdle,
518 QueueMode::Fifo,
519 ConsumePoint::OnRunComplete,
520 false,
521 );
522 }
523 #[test]
524 fn continuation_running() {
525 assert_cell(
526 InputKind::Continuation,
527 false,
528 ApplyMode::StageRunBoundary,
529 WakeMode::InterruptYielding,
530 QueueMode::Fifo,
531 ConsumePoint::OnRunComplete,
532 false,
533 );
534 }
535 #[test]
536 fn operation_idle() {
537 assert_cell(
538 InputKind::Operation,
539 true,
540 ApplyMode::Ignore,
541 WakeMode::None,
542 QueueMode::Priority,
543 ConsumePoint::OnAccept,
544 false,
545 );
546 }
547 #[test]
548 fn operation_running() {
549 assert_cell(
550 InputKind::Operation,
551 false,
552 ApplyMode::Ignore,
553 WakeMode::None,
554 QueueMode::Priority,
555 ConsumePoint::OnAccept,
556 false,
557 );
558 }
559
560 #[test]
561 fn resolve_via_input_object() {
562 use crate::input::*;
563 use chrono::Utc;
564 use meerkat_core::lifecycle::InputId;
565
566 let header = InputHeader {
567 id: InputId::new(),
568 timestamp: Utc::now(),
569 source: InputOrigin::Operator,
570 durability: InputDurability::Durable,
571 visibility: InputVisibility::default(),
572 idempotency_key: None,
573 supersession_key: None,
574 correlation_id: None,
575 };
576 let input = Input::Prompt(PromptInput {
577 header,
578 text: "hello".into(),
579 blocks: None,
580 typed_turn_appends: Vec::new(),
581 turn_metadata: None,
582 });
583 let decision = DefaultPolicyTable::resolve(&input, true);
584 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
585 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
586 }
587
588 #[test]
589 fn explicit_steer_metadata_maps_to_checkpoint_policy() {
590 use crate::input::*;
591 use chrono::Utc;
592 use meerkat_core::lifecycle::InputId;
593 use meerkat_core::lifecycle::run_primitive::RuntimeTurnMetadata;
594
595 let input = Input::Prompt(PromptInput {
596 header: InputHeader {
597 id: InputId::new(),
598 timestamp: Utc::now(),
599 source: InputOrigin::Operator,
600 durability: InputDurability::Durable,
601 visibility: InputVisibility::default(),
602 idempotency_key: None,
603 supersession_key: None,
604 correlation_id: None,
605 },
606 text: "hello".into(),
607 blocks: None,
608 typed_turn_appends: Vec::new(),
609 turn_metadata: Some(RuntimeTurnMetadata {
610 handling_mode: Some(meerkat_core::types::HandlingMode::Steer),
611 ..Default::default()
612 }),
613 });
614 let decision = DefaultPolicyTable::resolve(&input, true);
615 assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
616 assert_eq!(decision.drain_policy, DrainPolicy::SteerBatch);
617 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
618 }
619
620 #[test]
621 fn peer_message_running_wakes_after_current_turn() {
622 let decision =
623 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), false);
624 assert_eq!(
625 decision.wake_mode,
626 WakeMode::WakeIfIdle,
627 "peer_message while running must wake the runtime after the current turn"
628 );
629 }
630
631 #[test]
632 fn peer_request_running_wakes_after_current_turn() {
633 let decision =
634 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerRequest), false);
635 assert_eq!(
636 decision.wake_mode,
637 WakeMode::WakeIfIdle,
638 "peer_request while running must wake the runtime after the current turn"
639 );
640 }
641
642 #[test]
643 fn peer_message_idle_still_wakes() {
644 let decision =
646 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), true);
647 assert_eq!(
648 decision.wake_mode,
649 WakeMode::WakeIfIdle,
650 "peer_message while idle must use WakeIfIdle"
651 );
652 }
653
654 #[test]
655 fn peer_request_idle_still_wakes() {
656 let decision =
658 DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerRequest), true);
659 assert_eq!(
660 decision.wake_mode,
661 WakeMode::WakeIfIdle,
662 "peer_request while idle must use WakeIfIdle"
663 );
664 }
665
666 use crate::input::{
671 InputDurability, InputHeader, InputOrigin, InputVisibility, PeerConvention, PeerInput,
672 };
673 use chrono::Utc;
674 use meerkat_core::lifecycle::InputId;
675 use meerkat_core::types::HandlingMode;
676
677 fn make_peer_input(
678 convention: Option<PeerConvention>,
679 handling_mode: Option<HandlingMode>,
680 ) -> Input {
681 Input::Peer(PeerInput {
682 header: InputHeader {
683 id: InputId::new(),
684 timestamp: Utc::now(),
685 source: InputOrigin::Peer {
686 peer_id: "p".into(),
687 display_identity: None,
688 runtime_id: None,
689 },
690 durability: InputDurability::Durable,
691 visibility: InputVisibility::default(),
692 idempotency_key: None,
693 supersession_key: None,
694 correlation_id: None,
695 },
696 convention,
697 body: "test".into(),
698 payload: None,
699 blocks: None,
700 handling_mode,
701 })
702 }
703
704 #[test]
705 fn peer_message_with_explicit_queue_resolves_queue_semantics() {
706 let input = make_peer_input(Some(PeerConvention::Message), Some(HandlingMode::Queue));
707 let decision = DefaultPolicyTable::resolve(&input, true);
708 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
709 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
710
711 let running_decision = DefaultPolicyTable::resolve(&input, false);
712 assert_eq!(
713 running_decision.routing_disposition,
714 RoutingDisposition::Queue
715 );
716 assert_eq!(
717 running_decision.wake_mode,
718 WakeMode::None,
719 "explicit queue means next boundary, not interrupt-yielding, while the target is running"
720 );
721 }
722
723 #[test]
724 fn peer_message_with_explicit_steer_resolves_steer_semantics() {
725 let input = make_peer_input(Some(PeerConvention::Message), Some(HandlingMode::Steer));
726 let decision = DefaultPolicyTable::resolve(&input, true);
727 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
728 assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
729 }
730
731 #[test]
732 fn peer_request_with_explicit_steer_resolves_steer_semantics() {
733 let input = make_peer_input(
734 Some(PeerConvention::Request {
735 request_id: "r".into(),
736 intent: "i".into(),
737 }),
738 Some(HandlingMode::Steer),
739 );
740 let decision = DefaultPolicyTable::resolve(&input, false);
741 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
742 }
743
744 #[test]
745 fn peer_no_convention_with_explicit_steer_resolves_steer_semantics() {
746 let input = make_peer_input(None, Some(HandlingMode::Steer));
747 let decision = DefaultPolicyTable::resolve(&input, true);
748 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
749 }
750
751 #[test]
752 fn peer_message_without_override_preserves_kind_default() {
753 let input = make_peer_input(Some(PeerConvention::Message), None);
754 let decision = DefaultPolicyTable::resolve(&input, true);
755 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
757 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
758 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
759 }
760
761 #[test]
766 fn response_progress_with_handling_mode_falls_through_to_kind_default() {
767 let input = make_peer_input(
770 Some(PeerConvention::ResponseProgress {
771 request_id: "r".into(),
772 phase: crate::input::ResponseProgressPhase::InProgress,
773 }),
774 Some(HandlingMode::Steer),
775 );
776 let decision = DefaultPolicyTable::resolve(&input, true);
777 assert_eq!(decision.queue_mode, QueueMode::Coalesce);
780 assert_eq!(decision.apply_mode, ApplyMode::StageRunBoundary);
781 assert_eq!(decision.wake_mode, WakeMode::None);
782 }
783
784 #[test]
785 fn response_terminal_with_steer_gets_steer_semantics() {
786 let input = make_peer_input(
787 Some(PeerConvention::ResponseTerminal {
788 request_id: "r".into(),
789 status: crate::input::ResponseTerminalStatus::Completed,
790 }),
791 Some(HandlingMode::Steer),
792 );
793 let decision = DefaultPolicyTable::resolve(&input, true);
794 assert_eq!(decision.routing_disposition, RoutingDisposition::Steer);
795 assert_eq!(
796 decision.apply_mode,
797 ApplyMode::StageRunStart,
798 "terminal peer-response apply intent owns the context+reaction boundary; steer only changes urgency/lane"
799 );
800 assert_eq!(decision.drain_policy, DrainPolicy::SteerBatch);
801 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
802 assert!(decision.record_transcript);
803 }
804
805 #[test]
806 fn response_terminal_with_queue_handling_mode_gets_queue_semantics() {
807 let input = make_peer_input(
808 Some(PeerConvention::ResponseTerminal {
809 request_id: "r".into(),
810 status: crate::input::ResponseTerminalStatus::Completed,
811 }),
812 Some(HandlingMode::Queue),
813 );
814 let decision = DefaultPolicyTable::resolve(&input, true);
815 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
816 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
817 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
818
819 let running_decision = DefaultPolicyTable::resolve(&input, false);
820 assert_eq!(
821 running_decision.routing_disposition,
822 RoutingDisposition::Queue
823 );
824 assert_eq!(running_decision.apply_mode, ApplyMode::StageRunStart);
825 assert_eq!(running_decision.wake_mode, WakeMode::WakeIfIdle);
826 }
827
828 #[test]
829 fn response_terminal_without_handling_mode_keeps_kind_default() {
830 let input = make_peer_input(
831 Some(PeerConvention::ResponseTerminal {
832 request_id: "r".into(),
833 status: crate::input::ResponseTerminalStatus::Completed,
834 }),
835 None,
836 );
837 let decision = DefaultPolicyTable::resolve(&input, true);
838 assert_eq!(decision.routing_disposition, RoutingDisposition::Queue);
845 assert_eq!(decision.apply_mode, ApplyMode::StageRunStart);
846 assert_eq!(decision.wake_mode, WakeMode::WakeIfIdle);
847 }
848}