1use crate::affordance_key::AffordanceKey;
9use crate::arg_id::ArgumentId;
10use crate::error::Error;
11use argumentation_schemes::instance::SchemeInstance;
12use argumentation_schemes::registry::CatalogRegistry;
13use argumentation_weighted::types::Budget;
14use argumentation_weighted_bipolar::WeightedBipolarFramework;
15use std::collections::HashMap;
16use std::sync::Mutex;
17
18pub struct EncounterArgumentationState {
22 #[allow(dead_code)]
24 registry: CatalogRegistry,
25 framework: WeightedBipolarFramework<ArgumentId>,
27 actors_by_argument: HashMap<ArgumentId, Vec<String>>,
30 instances_by_argument: HashMap<ArgumentId, Vec<SchemeInstance>>,
32 argument_id_by_affordance: HashMap<AffordanceKey, ArgumentId>,
39 intensity: Mutex<Budget>,
45 errors: Mutex<Vec<Error>>,
51}
52
53impl EncounterArgumentationState {
54 #[must_use]
63 pub fn new(registry: CatalogRegistry) -> Self {
64 Self {
65 registry,
66 framework: WeightedBipolarFramework::new(),
67 actors_by_argument: HashMap::new(),
68 instances_by_argument: HashMap::new(),
69 argument_id_by_affordance: HashMap::new(),
70 intensity: Mutex::new(Budget::zero()),
71 errors: Mutex::new(Vec::new()),
72 }
73 }
74
75 #[must_use]
77 pub fn intensity(&self) -> Budget {
78 *self.intensity_guard()
79 }
80
81 fn intensity_guard(&self) -> std::sync::MutexGuard<'_, Budget> {
89 self.intensity.lock().unwrap_or_else(|e| e.into_inner())
90 }
91
92 fn errors_guard(&self) -> std::sync::MutexGuard<'_, Vec<Error>> {
99 self.errors.lock().unwrap_or_else(|e| e.into_inner())
100 }
101
102 #[must_use]
104 pub fn argument_count(&self) -> usize {
105 self.framework.argument_count()
106 }
107
108 #[must_use]
110 pub fn edge_count(&self) -> usize {
111 self.framework.edge_count()
112 }
113
114 pub fn add_scheme_instance(
120 &mut self,
121 actor: &str,
122 instance: SchemeInstance,
123 ) -> ArgumentId {
124 let id: ArgumentId = (&instance.conclusion).into();
125 self.framework.add_argument(id.clone());
126 self.actors_by_argument
127 .entry(id.clone())
128 .or_default()
129 .push(actor.to_string());
130 self.instances_by_argument
131 .entry(id.clone())
132 .or_default()
133 .push(instance);
134 id
135 }
136
137 pub fn add_scheme_instance_for_affordance(
158 &mut self,
159 actor: &str,
160 affordance_name: &str,
161 bindings: &std::collections::HashMap<String, String>,
162 instance: SchemeInstance,
163 ) -> ArgumentId {
164 let id = self.add_scheme_instance(actor, instance);
165 let key = AffordanceKey::new(actor, affordance_name, bindings);
166 self.argument_id_by_affordance.insert(key, id.clone());
167 id
168 }
169
170 #[must_use]
173 pub fn argument_id_for(
174 &self,
175 key: &AffordanceKey,
176 ) -> Option<ArgumentId> {
177 self.argument_id_by_affordance.get(key).cloned()
178 }
179
180 #[must_use]
183 pub fn actors_for(&self, id: &ArgumentId) -> &[String] {
184 self.actors_by_argument
185 .get(id)
186 .map(Vec::as_slice)
187 .unwrap_or(&[])
188 }
189
190 #[must_use]
196 pub fn actors_by_argument(&self) -> &HashMap<ArgumentId, Vec<String>> {
197 &self.actors_by_argument
198 }
199
200 #[must_use]
203 pub fn instances_for(&self, id: &ArgumentId) -> &[SchemeInstance] {
204 self.instances_by_argument
205 .get(id)
206 .map(Vec::as_slice)
207 .unwrap_or(&[])
208 }
209
210 #[must_use]
224 pub fn attackers_of(&self, target: &ArgumentId) -> Vec<ArgumentId> {
225 self.framework
226 .attacks()
227 .filter(|atk| &atk.target == target)
228 .map(|atk| atk.attacker.clone())
229 .collect()
230 }
231
232 pub fn has_accepted_counter_by(
260 &self,
261 responder: &str,
262 target: &ArgumentId,
263 ) -> Result<bool, Error> {
264 for attacker in self.attackers_of(target) {
265 let asserted_by_responder = self
266 .actors_for(&attacker)
267 .iter()
268 .any(|a| a == responder);
269 if !asserted_by_responder {
270 continue;
271 }
272 if self.is_credulously_accepted(&attacker)? {
273 return Ok(true);
274 }
275 }
276 Ok(false)
277 }
278
279 pub fn add_weighted_attack(
283 &mut self,
284 attacker: &ArgumentId,
285 target: &ArgumentId,
286 weight: f64,
287 ) -> Result<(), Error> {
288 self.framework
289 .add_weighted_attack(attacker.clone(), target.clone(), weight)?;
290 Ok(())
291 }
292
293 pub fn add_weighted_support(
297 &mut self,
298 supporter: &ArgumentId,
299 supported: &ArgumentId,
300 weight: f64,
301 ) -> Result<(), Error> {
302 self.framework
303 .add_weighted_support(supporter.clone(), supported.clone(), weight)?;
304 Ok(())
305 }
306
307 #[must_use]
310 pub fn at_intensity(self, intensity: Budget) -> Self {
311 *self.intensity_guard() = intensity;
312 self
313 }
314
315 pub fn set_intensity(&self, intensity: Budget) {
323 *self.intensity_guard() = intensity;
324 }
325
326 pub fn is_credulously_accepted(&self, arg: &ArgumentId) -> Result<bool, Error> {
330 Ok(argumentation_weighted_bipolar::is_credulously_accepted_at(
331 &self.framework,
332 arg,
333 self.intensity(),
334 )?)
335 }
336
337 pub fn is_skeptically_accepted(&self, arg: &ArgumentId) -> Result<bool, Error> {
341 Ok(argumentation_weighted_bipolar::is_skeptically_accepted_at(
342 &self.framework,
343 arg,
344 self.intensity(),
345 )?)
346 }
347
348 pub fn coalitions(&self) -> Result<Vec<argumentation_bipolar::Coalition<ArgumentId>>, Error> {
357 let residuals = argumentation_weighted_bipolar::wbipolar_residuals(
360 &self.framework,
361 Budget::zero(),
362 )?;
363 let bipolar = residuals
364 .into_iter()
365 .next()
366 .expect("zero-budget residual always includes the empty subset");
367 Ok(argumentation_bipolar::detect_coalitions(&bipolar))
368 }
369
370 #[must_use]
379 pub fn drain_errors(&self) -> Vec<Error> {
380 std::mem::take(&mut *self.errors_guard())
381 }
382
383 pub(crate) fn record_error(&self, err: Error) {
390 self.errors_guard().push(err);
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397 use argumentation_schemes::catalog::default_catalog;
398
399 #[test]
400 fn new_state_is_empty() {
401 let state = EncounterArgumentationState::new(default_catalog());
402 assert_eq!(state.argument_count(), 0);
403 assert_eq!(state.edge_count(), 0);
404 }
405
406 #[test]
407 fn new_state_has_zero_intensity() {
408 let state = EncounterArgumentationState::new(default_catalog());
409 assert_eq!(state.intensity().value(), 0.0);
410 }
411
412 #[test]
413 fn add_scheme_instance_creates_argument_node() {
414 let registry = default_catalog();
415 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
416 let instance = scheme
417 .instantiate(
418 &[
419 ("expert".to_string(), "alice".to_string()),
420 ("domain".to_string(), "military".to_string()),
421 ("claim".to_string(), "fortify_east".to_string()),
422 ]
423 .into_iter()
424 .collect(),
425 )
426 .unwrap();
427
428 let mut state = EncounterArgumentationState::new(registry);
429 let id = state.add_scheme_instance("alice", instance);
430
431 assert_eq!(id.as_str(), "fortify_east");
432 assert_eq!(state.argument_count(), 1);
433 }
434
435 #[test]
436 fn add_scheme_instance_associates_actor_and_instance() {
437 let registry = default_catalog();
438 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
439 let instance = scheme
440 .instantiate(
441 &[
442 ("expert".to_string(), "alice".to_string()),
443 ("domain".to_string(), "military".to_string()),
444 ("claim".to_string(), "fortify_east".to_string()),
445 ]
446 .into_iter()
447 .collect(),
448 )
449 .unwrap();
450 let mut state = EncounterArgumentationState::new(registry);
451 let id = state.add_scheme_instance("alice", instance);
452 assert_eq!(state.actors_for(&id), &["alice".to_string()]);
453 assert_eq!(state.instances_for(&id).len(), 1);
454 }
455
456 #[test]
457 fn add_two_instances_with_same_conclusion_share_node() {
458 let registry = default_catalog();
459 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
460
461 let inst1 = scheme
462 .instantiate(
463 &[
464 ("expert".to_string(), "alice".to_string()),
465 ("domain".to_string(), "military".to_string()),
466 ("claim".to_string(), "fortify_east".to_string()),
467 ]
468 .into_iter()
469 .collect(),
470 )
471 .unwrap();
472 let inst2 = scheme
473 .instantiate(
474 &[
475 ("expert".to_string(), "bob".to_string()),
476 ("domain".to_string(), "logistics".to_string()),
477 ("claim".to_string(), "fortify_east".to_string()),
478 ]
479 .into_iter()
480 .collect(),
481 )
482 .unwrap();
483
484 let mut state = EncounterArgumentationState::new(registry);
485 let id1 = state.add_scheme_instance("alice", inst1);
486 let id2 = state.add_scheme_instance("bob", inst2);
487 assert_eq!(id1, id2);
488 assert_eq!(state.argument_count(), 1);
489 assert_eq!(
490 state.actors_for(&id1),
491 &["alice".to_string(), "bob".to_string()]
492 );
493 assert_eq!(state.instances_for(&id1).len(), 2);
494 }
495
496 #[test]
497 fn add_weighted_attack_propagates_to_framework() {
498 let mut state = EncounterArgumentationState::new(default_catalog());
499 let a = ArgumentId::new("a");
500 let b = ArgumentId::new("b");
501 state.add_weighted_attack(&a, &b, 0.5).unwrap();
502 assert_eq!(state.edge_count(), 1);
503 }
504
505 #[test]
506 fn add_weighted_support_propagates_to_framework() {
507 let mut state = EncounterArgumentationState::new(default_catalog());
508 let a = ArgumentId::new("a");
509 let b = ArgumentId::new("b");
510 state.add_weighted_support(&a, &b, 0.5).unwrap();
511 assert_eq!(state.edge_count(), 1);
512 }
513
514 #[test]
515 fn add_weighted_support_rejects_self_support() {
516 let mut state = EncounterArgumentationState::new(default_catalog());
517 let a = ArgumentId::new("a");
518 let err = state.add_weighted_support(&a, &a, 0.5).unwrap_err();
519 assert!(matches!(err, Error::WeightedBipolar(_)));
520 }
521
522 #[test]
523 fn add_weighted_attack_rejects_invalid_weight() {
524 let mut state = EncounterArgumentationState::new(default_catalog());
525 let a = ArgumentId::new("a");
526 let b = ArgumentId::new("b");
527 let err = state.add_weighted_attack(&a, &b, -0.1).unwrap_err();
528 assert!(matches!(err, Error::WeightedBipolar(_)));
529 }
530
531 #[test]
532 fn at_intensity_sets_budget() {
533 let state = EncounterArgumentationState::new(default_catalog())
534 .at_intensity(Budget::new(0.5).unwrap());
535 assert_eq!(state.intensity().value(), 0.5);
536 }
537
538 #[test]
539 fn at_intensity_is_chainable_with_add() {
540 let mut state = EncounterArgumentationState::new(default_catalog())
541 .at_intensity(Budget::new(0.25).unwrap());
542 state
543 .add_weighted_attack(&ArgumentId::new("a"), &ArgumentId::new("b"), 0.3)
544 .unwrap();
545 assert_eq!(state.intensity().value(), 0.25);
546 assert_eq!(state.edge_count(), 1);
547 }
548
549 #[test]
550 fn unattacked_argument_is_credulously_accepted() {
551 let mut state = EncounterArgumentationState::new(default_catalog());
552 let a = ArgumentId::new("a");
553 state.add_weighted_attack(&a, &ArgumentId::new("unused"), 0.0).unwrap();
554 assert!(state.is_credulously_accepted(&a).unwrap());
556 }
557
558 #[test]
559 fn attacked_argument_is_not_credulously_accepted_at_zero_intensity() {
560 let mut state = EncounterArgumentationState::new(default_catalog());
561 let a = ArgumentId::new("a");
562 let b = ArgumentId::new("b");
563 state.add_weighted_attack(&a, &b, 0.5).unwrap();
564 assert!(!state.is_credulously_accepted(&b).unwrap());
566 }
567
568 #[test]
569 fn raising_intensity_flips_acceptance_when_budget_covers_attack() {
570 let mut state = EncounterArgumentationState::new(default_catalog())
571 .at_intensity(Budget::new(0.5).unwrap());
572 let a = ArgumentId::new("a");
573 let b = ArgumentId::new("b");
574 state.add_weighted_attack(&a, &b, 0.4).unwrap();
575 assert!(state.is_credulously_accepted(&b).unwrap());
578 }
579
580 #[test]
581 fn skeptical_is_stricter_than_credulous() {
582 let mut state = EncounterArgumentationState::new(default_catalog())
583 .at_intensity(Budget::new(0.5).unwrap());
584 let a = ArgumentId::new("a");
585 let b = ArgumentId::new("b");
586 state.add_weighted_attack(&a, &b, 0.4).unwrap();
587 assert!(state.is_credulously_accepted(&b).unwrap());
590 assert!(!state.is_skeptically_accepted(&b).unwrap());
591 }
592
593 #[test]
594 fn no_supports_means_all_coalitions_are_singletons() {
595 let mut state = EncounterArgumentationState::new(default_catalog());
596 state.add_weighted_attack(&ArgumentId::new("a"), &ArgumentId::new("b"), 0.5).unwrap();
597 let coalitions = state.coalitions().unwrap();
598 assert!(coalitions.iter().all(|c| c.members.len() == 1));
602 }
603
604 #[test]
605 fn mutual_support_forms_coalition() {
606 let mut state = EncounterArgumentationState::new(default_catalog());
607 let a = ArgumentId::new("a");
608 let b = ArgumentId::new("b");
609 state.add_weighted_support(&a, &b, 0.5).unwrap();
610 state.add_weighted_support(&b, &a, 0.5).unwrap();
611 let coalitions = state.coalitions().unwrap();
612 assert!(coalitions.iter().any(|c| c.members.len() == 2
614 && c.members.contains(&a)
615 && c.members.contains(&b)));
616 }
617
618 #[test]
619 fn set_intensity_mutates_through_shared_ref() {
620 let state = EncounterArgumentationState::new(default_catalog())
621 .at_intensity(Budget::new(0.2).unwrap());
622 assert_eq!(state.intensity().value(), 0.2);
623 state.set_intensity(Budget::new(0.6).unwrap());
626 assert_eq!(state.intensity().value(), 0.6);
627 }
628
629 #[test]
630 fn intensity_is_mutable_from_two_shared_refs_in_sequence() {
631 let state = EncounterArgumentationState::new(default_catalog());
632 fn bump(s: &EncounterArgumentationState, b: f64) {
633 s.set_intensity(Budget::new(b).unwrap());
634 }
635 bump(&state, 0.3);
636 bump(&state, 0.5);
637 assert_eq!(state.intensity().value(), 0.5);
638 }
639
640 #[test]
641 fn add_scheme_instance_for_affordance_indexes_by_key() {
642 let registry = default_catalog();
643 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
644 let mut bindings = std::collections::HashMap::new();
645 bindings.insert("expert".to_string(), "alice".to_string());
646 bindings.insert("domain".to_string(), "military".to_string());
647 bindings.insert("claim".to_string(), "fortify_east".to_string());
648 let instance = scheme.instantiate(&bindings).unwrap();
649
650 let mut state = EncounterArgumentationState::new(registry);
651 let id = state.add_scheme_instance_for_affordance(
652 "alice",
653 "argue_fortify_east",
654 &bindings,
655 instance,
656 );
657
658 let key = AffordanceKey::new("alice", "argue_fortify_east", &bindings);
659 let looked_up = state.argument_id_for(&key);
660 assert_eq!(looked_up, Some(id));
661 }
662
663 #[test]
664 fn argument_id_for_returns_none_for_unseeded_key() {
665 let bindings = std::collections::HashMap::new();
666 let state = EncounterArgumentationState::new(default_catalog());
667 let key = AffordanceKey::new("nobody", "nothing", &bindings);
668 assert_eq!(state.argument_id_for(&key), None);
669 }
670
671 #[test]
672 fn add_scheme_instance_for_affordance_is_consistent_with_add_scheme_instance() {
673 let registry = default_catalog();
674 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
675 let mut bindings = std::collections::HashMap::new();
676 bindings.insert("expert".to_string(), "alice".to_string());
677 bindings.insert("domain".to_string(), "military".to_string());
678 bindings.insert("claim".to_string(), "fortify_east".to_string());
679 let instance = scheme.instantiate(&bindings).unwrap();
680 let mut state = EncounterArgumentationState::new(registry);
681 let id = state.add_scheme_instance_for_affordance(
682 "alice",
683 "argue_fortify_east",
684 &bindings,
685 instance,
686 );
687 assert_eq!(state.actors_for(&id), &["alice".to_string()]);
688 assert_eq!(state.instances_for(&id).len(), 1);
689 }
690
691 #[test]
692 fn two_distinct_affordance_keys_with_same_conclusion_point_at_shared_id() {
693 let registry = default_catalog();
694 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
695
696 let mut alice_bindings = std::collections::HashMap::new();
697 alice_bindings.insert("expert".to_string(), "alice".to_string());
698 alice_bindings.insert("domain".to_string(), "military".to_string());
699 alice_bindings.insert("claim".to_string(), "fortify_east".to_string());
700 let alice_instance = scheme.instantiate(&alice_bindings).unwrap();
701
702 let mut bob_bindings = std::collections::HashMap::new();
703 bob_bindings.insert("expert".to_string(), "bob".to_string());
704 bob_bindings.insert("domain".to_string(), "logistics".to_string());
705 bob_bindings.insert("claim".to_string(), "fortify_east".to_string());
706 let bob_instance = scheme.instantiate(&bob_bindings).unwrap();
707
708 let mut state = EncounterArgumentationState::new(registry);
709 let alice_id = state.add_scheme_instance_for_affordance(
710 "alice",
711 "argue_fortify_east",
712 &alice_bindings,
713 alice_instance,
714 );
715 let bob_id = state.add_scheme_instance_for_affordance(
716 "bob",
717 "second_expert_opinion",
718 &bob_bindings,
719 bob_instance,
720 );
721
722 assert_eq!(alice_id, bob_id, "same conclusion literal → shared ArgumentId");
723 assert_eq!(state.argument_count(), 1);
724
725 let alice_key = AffordanceKey::new("alice", "argue_fortify_east", &alice_bindings);
726 let bob_key = AffordanceKey::new("bob", "second_expert_opinion", &bob_bindings);
727 assert_eq!(state.argument_id_for(&alice_key), Some(alice_id.clone()));
728 assert_eq!(state.argument_id_for(&bob_key), Some(alice_id.clone()));
729
730 assert_eq!(
731 state.actors_for(&alice_id),
732 &["alice".to_string(), "bob".to_string()]
733 );
734 }
735
736 #[test]
737 fn attackers_of_returns_all_direct_attackers() {
738 let mut state = EncounterArgumentationState::new(default_catalog());
739 let target = ArgumentId::new("target");
740 let a1 = ArgumentId::new("a1");
741 let a2 = ArgumentId::new("a2");
742 let unrelated = ArgumentId::new("unrelated");
743 state.add_weighted_attack(&a1, &target, 0.5).unwrap();
744 state.add_weighted_attack(&a2, &target, 0.3).unwrap();
745 state.add_weighted_attack(&unrelated, &ArgumentId::new("x"), 0.5).unwrap();
746 let attackers: std::collections::HashSet<_> =
747 state.attackers_of(&target).into_iter().collect();
748 assert_eq!(attackers.len(), 2);
749 assert!(attackers.contains(&a1));
750 assert!(attackers.contains(&a2));
751 }
752
753 #[test]
754 fn attackers_of_returns_empty_for_unattacked() {
755 let state = EncounterArgumentationState::new(default_catalog());
756 let lonely = ArgumentId::new("lonely");
757 assert!(state.attackers_of(&lonely).is_empty());
758 }
759
760 #[test]
761 fn attackers_of_preserves_duplicate_edges() {
762 let mut state = EncounterArgumentationState::new(default_catalog());
763 let target = ArgumentId::new("target");
764 let a1 = ArgumentId::new("a1");
765 state.add_weighted_attack(&a1, &target, 0.5).unwrap();
766 state.add_weighted_attack(&a1, &target, 0.7).unwrap();
767 assert_eq!(state.attackers_of(&target).len(), 2);
768 }
769
770 #[test]
771 fn has_accepted_counter_by_detects_responder_attacker_at_beta() {
772 let registry = default_catalog();
773 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
774 let mut target_bindings = std::collections::HashMap::new();
775 target_bindings.insert("expert".to_string(), "alice".to_string());
776 target_bindings.insert("domain".to_string(), "military".to_string());
777 target_bindings.insert("claim".to_string(), "fortify_east".to_string());
778 let target_instance = scheme.instantiate(&target_bindings).unwrap();
779 let mut counter_bindings = std::collections::HashMap::new();
780 counter_bindings.insert("expert".to_string(), "bob".to_string());
781 counter_bindings.insert("domain".to_string(), "logistics".to_string());
782 counter_bindings.insert("claim".to_string(), "abandon_east".to_string());
783 let counter_instance = scheme.instantiate(&counter_bindings).unwrap();
784
785 let mut state = EncounterArgumentationState::new(registry);
786 let target_id = state.add_scheme_instance("alice", target_instance);
787 let counter_id = state.add_scheme_instance("bob", counter_instance);
788 state.add_weighted_attack(&counter_id, &target_id, 0.5).unwrap();
789
790 assert!(state.has_accepted_counter_by("bob", &target_id).unwrap());
794 assert!(!state.has_accepted_counter_by("alice", &target_id).unwrap());
795 }
796
797 #[test]
798 fn drain_errors_round_trips_stashed_errors() {
799 let state = EncounterArgumentationState::new(default_catalog());
800 assert!(state.drain_errors().is_empty());
801 state.record_error(Error::SchemeNotFound("x".into()));
802 state.record_error(Error::SchemeNotFound("y".into()));
803 let errs = state.drain_errors();
804 assert_eq!(errs.len(), 2);
805 assert!(matches!(&errs[0], Error::SchemeNotFound(s) if s == "x"));
806 assert!(matches!(&errs[1], Error::SchemeNotFound(s) if s == "y"));
807 assert!(state.drain_errors().is_empty());
809 }
810
811 #[test]
812 fn actors_by_argument_exposes_actor_map() {
813 let registry = default_catalog();
814 let scheme = registry.by_key("argument_from_expert_opinion").unwrap();
815 let instance = scheme
816 .instantiate(
817 &[
818 ("expert".to_string(), "alice".to_string()),
819 ("domain".to_string(), "military".to_string()),
820 ("claim".to_string(), "fortify_east".to_string()),
821 ]
822 .into_iter()
823 .collect(),
824 )
825 .unwrap();
826 let mut state = EncounterArgumentationState::new(registry);
827 let id = state.add_scheme_instance("alice", instance);
828 let map = state.actors_by_argument();
829 assert_eq!(map.len(), 1);
830 assert_eq!(map.get(&id), Some(&vec!["alice".to_string()]));
831 }
832
833 #[test]
834 fn actors_by_argument_is_empty_on_new_state() {
835 let state = EncounterArgumentationState::new(default_catalog());
836 assert!(state.actors_by_argument().is_empty());
837 }
838
839 #[test]
840 fn state_is_send_and_sync() {
841 fn assert_send_sync<T: Send + Sync>() {}
848 assert_send_sync::<EncounterArgumentationState>();
849 }
850}