1use serde::{Deserialize, Serialize};
11use xpile_contracts::ContractId;
12use xpile_meta_hir::Module;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
16pub enum Target {
17 Rust,
19 Ruchy,
21 Ptx,
23 Wgsl,
25 Spirv,
27 Lean,
31 Shell,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
42pub enum Profile {
43 RustOut,
45 RuchyOut,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub enum HwProfile {
54 Ptx {
55 compute_capability: String,
57 },
58 Wgsl {
59 features: Vec<String>,
61 },
62 Spirv {
63 version: (u32, u32),
64 },
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct BackendConfig {
70 pub target: Target,
71 pub profile: Profile,
72 pub hardware: Option<HwProfile>,
73}
74
75#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84pub struct Artifact {
85 pub primary: String,
88 pub sidecars: Vec<(String, Vec<u8>)>,
90 pub citations: Vec<ContractId>,
93 #[serde(default = "default_quorum_status")]
102 pub quorum_status: QuorumStatus,
103}
104
105fn default_quorum_status() -> QuorumStatus {
106 QuorumStatus::Single {
107 emitter: "unknown".to_string(),
108 }
109}
110
111#[derive(Debug, thiserror::Error)]
112pub enum BackendError {
113 #[error("unsupported target: {0:?}")]
114 UnsupportedTarget(Target),
115 #[error("missing hardware profile for target {0:?}")]
116 MissingHardware(Target),
117 #[error("lowering error: {0}")]
118 Lower(String),
119 #[error("compile-contract citation missing for emitted construct: {0}")]
120 MissingCompileContractCitation(String),
121}
122
123pub trait Backend: Send + Sync {
125 fn name(&self) -> &'static str;
127
128 fn targets(&self) -> &[Target];
131
132 fn lower(&self, module: &Module, config: &BackendConfig) -> Result<Artifact, BackendError>;
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
154#[serde(rename_all = "snake_case")]
155pub enum EmitterRole {
156 General,
162 Specialist,
167}
168
169#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173#[serde(rename_all = "snake_case", tag = "kind")]
174pub enum QuorumPolicy {
175 PreferSpecialist,
178 DiffExec {
184 tolerance: f64,
187 },
188 Strict,
193}
194
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198#[serde(tag = "kind", rename_all = "snake_case")]
199pub enum QuorumStatus {
200 Single { emitter: String },
204 Multi {
207 emitters: Vec<String>,
208 diff_exec: Option<DiffExecResult>,
209 },
210}
211
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216#[serde(tag = "kind", rename_all = "snake_case")]
217pub enum DiffExecResult {
218 Match { max_abs_diff: f64 },
221 Divergent { max_abs_diff: f64, tolerance: f64 },
224 NotRun { reason: String },
228}
229
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
237pub struct ViaEntry {
238 pub emitter: String,
240 pub role: EmitterRole,
242 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub crate_name: Option<String>,
245 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub cross_repo: Option<String>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub shape_filter: Option<String>,
255}
256
257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269pub struct EmittedText {
270 pub primary: String,
271 #[serde(default, skip_serializing_if = "Vec::is_empty")]
272 pub citations: Vec<ContractId>,
273}
274
275pub trait TargetEmitter: Send + Sync {
280 fn name(&self) -> &str;
282
283 fn try_emit(
288 &self,
289 module: &Module,
290 config: &BackendConfig,
291 ) -> Option<Result<EmittedText, BackendError>>;
292}
293
294pub struct MultiEmitterBackend {
299 pub target: Target,
302 pub general: Box<dyn TargetEmitter>,
305 pub specialist: Option<Box<dyn TargetEmitter>>,
308 pub quorum_policy: QuorumPolicy,
310}
311
312impl MultiEmitterBackend {
313 pub fn new_single(target: Target, general: Box<dyn TargetEmitter>) -> Self {
314 Self {
315 target,
316 general,
317 specialist: None,
318 quorum_policy: QuorumPolicy::PreferSpecialist,
319 }
320 }
321
322 pub fn new_with_specialist(
323 target: Target,
324 general: Box<dyn TargetEmitter>,
325 specialist: Box<dyn TargetEmitter>,
326 quorum_policy: QuorumPolicy,
327 ) -> Self {
328 Self {
329 target,
330 general,
331 specialist: Some(specialist),
332 quorum_policy,
333 }
334 }
335}
336
337impl Backend for MultiEmitterBackend {
338 fn name(&self) -> &'static str {
339 "multi-emitter"
342 }
343
344 fn targets(&self) -> &[Target] {
345 std::slice::from_ref(&self.target)
346 }
347
348 fn lower(&self, module: &Module, config: &BackendConfig) -> Result<Artifact, BackendError> {
349 let general_result = self.general.try_emit(module, config).ok_or_else(|| {
350 BackendError::Lower(format!(
351 "general emitter {} must always match contract-conforming input",
352 self.general.name()
353 ))
354 })??;
355
356 let specialist_result = self.specialist.as_ref().and_then(|s| {
357 s.try_emit(module, config)
358 .map(|r| (s.name().to_string(), r))
359 });
360
361 match specialist_result {
362 None => {
363 Ok(Artifact {
365 primary: general_result.primary,
366 sidecars: Vec::new(),
367 citations: general_result.citations,
368 quorum_status: QuorumStatus::Single {
369 emitter: self.general.name().to_string(),
370 },
371 })
372 }
373 Some((specialist_name, specialist_emit)) => {
374 let specialist_text = specialist_emit?;
375 match &self.quorum_policy {
376 QuorumPolicy::PreferSpecialist => {
377 Ok(Artifact {
380 primary: specialist_text.primary,
381 sidecars: Vec::new(),
382 citations: specialist_text.citations,
383 quorum_status: QuorumStatus::Single {
384 emitter: specialist_name,
385 },
386 })
387 }
388 QuorumPolicy::Strict => {
389 let diff_exec = if general_result.primary == specialist_text.primary {
391 Some(DiffExecResult::Match { max_abs_diff: 0.0 })
392 } else {
393 Some(DiffExecResult::Divergent {
394 max_abs_diff: f64::INFINITY,
395 tolerance: 0.0,
396 })
397 };
398 Ok(Artifact {
399 primary: general_result.primary.clone(),
400 sidecars: vec![(
401 "specialist_emission".to_string(),
402 specialist_text.primary.into_bytes(),
403 )],
404 citations: general_result.citations,
405 quorum_status: QuorumStatus::Multi {
406 emitters: vec![self.general.name().to_string(), specialist_name],
407 diff_exec,
408 },
409 })
410 }
411 QuorumPolicy::DiffExec { tolerance } => {
412 Ok(Artifact {
416 primary: general_result.primary.clone(),
417 sidecars: vec![(
418 "specialist_emission".to_string(),
419 specialist_text.primary.into_bytes(),
420 )],
421 citations: general_result.citations,
422 quorum_status: QuorumStatus::Multi {
423 emitters: vec![self.general.name().to_string(), specialist_name],
424 diff_exec: Some(DiffExecResult::NotRun {
425 reason: format!(
426 "DiffExec engine not yet implemented (tolerance was {tolerance})"
427 ),
428 }),
429 },
430 })
431 }
432 }
433 }
434 }
435 }
436}
437
438#[cfg(test)]
439mod quorum_scaffolding_tests {
440 use super::*;
441
442 #[test]
443 fn emitter_role_serde_round_trip() {
444 let general = EmitterRole::General;
445 let s = serde_json::to_string(&general).unwrap();
446 assert_eq!(s, "\"general\"");
447 let back: EmitterRole = serde_json::from_str(&s).unwrap();
448 assert_eq!(back, general);
449
450 let specialist = EmitterRole::Specialist;
451 let s = serde_json::to_string(&specialist).unwrap();
452 assert_eq!(s, "\"specialist\"");
453 }
454
455 #[test]
456 fn quorum_policy_diff_exec_carries_tolerance() {
457 let policy = QuorumPolicy::DiffExec { tolerance: 1.0e-3 };
458 let s = serde_json::to_string(&policy).unwrap();
459 assert!(s.contains("diff_exec"));
460 assert!(s.contains("0.001"));
461 let back: QuorumPolicy = serde_json::from_str(&s).unwrap();
462 assert_eq!(back, policy);
463 }
464
465 #[test]
466 fn quorum_status_multi_records_emitters_and_diff() {
467 let status = QuorumStatus::Multi {
468 emitters: vec!["rustc_codegen_nvvm".into(), "aprender-gpu".into()],
469 diff_exec: Some(DiffExecResult::Match {
470 max_abs_diff: 1.3e-4,
471 }),
472 };
473 let s = serde_json::to_string(&status).unwrap();
474 assert!(s.contains("multi"));
475 assert!(s.contains("rustc_codegen_nvvm"));
476 assert!(s.contains("aprender-gpu"));
477 }
478
479 #[test]
480 fn diff_exec_divergent_carries_both_diff_and_tolerance() {
481 let r = DiffExecResult::Divergent {
482 max_abs_diff: 0.5,
483 tolerance: 0.001,
484 };
485 let s = serde_json::to_string(&r).unwrap();
486 let back: DiffExecResult = serde_json::from_str(&s).unwrap();
487 assert_eq!(back, r);
488 }
489
490 #[test]
491 fn via_entry_general_has_no_specialist_fields() {
492 let v = ViaEntry {
493 emitter: "rustc_codegen_nvvm".into(),
494 role: EmitterRole::General,
495 crate_name: Some("xpile-ptx-codegen".into()),
496 cross_repo: None,
497 shape_filter: None,
498 };
499 let s = serde_json::to_string(&v).unwrap();
500 assert!(!s.contains("cross_repo"));
502 assert!(!s.contains("shape_filter"));
503 assert!(s.contains("general"));
504 }
505
506 #[test]
507 fn via_entry_specialist_carries_cross_repo_and_shape_filter() {
508 let v = ViaEntry {
509 emitter: "aprender-gpu".into(),
510 role: EmitterRole::Specialist,
511 crate_name: None,
512 cross_repo: Some("aprender".into()),
513 shape_filter: Some("gemm_fp16_mma_64x128".into()),
514 };
515 let s = serde_json::to_string(&v).unwrap();
516 assert!(s.contains("specialist"));
517 assert!(s.contains("aprender"));
518 assert!(s.contains("gemm_fp16_mma_64x128"));
519 }
520
521 #[test]
525 fn artifact_quorum_status_defaults_for_older_payloads() {
526 let legacy_json = r#"{"primary":"// test","sidecars":[],"citations":[]}"#;
527 let a: Artifact = serde_json::from_str(legacy_json).unwrap();
528 assert_eq!(
529 a.quorum_status,
530 QuorumStatus::Single {
531 emitter: "unknown".to_string()
532 }
533 );
534 }
535
536 #[test]
539 fn artifact_quorum_status_single_round_trips() {
540 let a = Artifact {
541 primary: "// test".into(),
542 sidecars: Vec::new(),
543 citations: Vec::new(),
544 quorum_status: QuorumStatus::Single {
545 emitter: "xpile-rust-codegen".to_string(),
546 },
547 };
548 let s = serde_json::to_string(&a).unwrap();
549 let back: Artifact = serde_json::from_str(&s).unwrap();
550 assert_eq!(back, a);
551 }
552
553 struct MockGeneral {
559 name: &'static str,
560 body: String,
561 }
562 impl TargetEmitter for MockGeneral {
563 fn name(&self) -> &str {
564 self.name
565 }
566 fn try_emit(
567 &self,
568 _module: &Module,
569 _config: &BackendConfig,
570 ) -> Option<Result<EmittedText, BackendError>> {
571 Some(Ok(EmittedText {
572 primary: self.body.clone(),
573 citations: Vec::new(),
574 }))
575 }
576 }
577
578 struct MockSpecialist {
581 name: &'static str,
582 matches: bool,
583 body: String,
584 }
585 impl TargetEmitter for MockSpecialist {
586 fn name(&self) -> &str {
587 self.name
588 }
589 fn try_emit(
590 &self,
591 _module: &Module,
592 _config: &BackendConfig,
593 ) -> Option<Result<EmittedText, BackendError>> {
594 if self.matches {
595 Some(Ok(EmittedText {
596 primary: self.body.clone(),
597 citations: Vec::new(),
598 }))
599 } else {
600 None
601 }
602 }
603 }
604
605 fn dummy_module() -> Module {
606 Module {
607 name: "test".into(),
608 source_lang: xpile_meta_hir::SourceLang::Rust,
609 items: Vec::new(),
610 ffi_boundaries: Vec::new(),
611 }
612 }
613
614 fn dummy_config() -> BackendConfig {
615 BackendConfig {
616 target: Target::Ptx,
617 profile: Profile::RustOut,
618 hardware: None,
619 }
620 }
621
622 #[test]
623 fn multi_emitter_specialist_missing_falls_back_to_general() {
624 let backend = MultiEmitterBackend::new_single(
625 Target::Ptx,
626 Box::new(MockGeneral {
627 name: "general",
628 body: "general output".into(),
629 }),
630 );
631 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
632 assert_eq!(artifact.primary, "general output");
633 assert_eq!(
634 artifact.quorum_status,
635 QuorumStatus::Single {
636 emitter: "general".to_string()
637 }
638 );
639 }
640
641 #[test]
642 fn multi_emitter_specialist_unmatched_falls_back_to_general() {
643 let backend = MultiEmitterBackend::new_with_specialist(
644 Target::Ptx,
645 Box::new(MockGeneral {
646 name: "general",
647 body: "general output".into(),
648 }),
649 Box::new(MockSpecialist {
650 name: "specialist",
651 matches: false,
652 body: "specialist output".into(),
653 }),
654 QuorumPolicy::DiffExec { tolerance: 1e-3 },
655 );
656 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
657 assert_eq!(artifact.primary, "general output");
658 assert_eq!(
660 artifact.quorum_status,
661 QuorumStatus::Single {
662 emitter: "general".to_string()
663 }
664 );
665 }
666
667 #[test]
668 fn multi_emitter_prefer_specialist_uses_specialist_output() {
669 let backend = MultiEmitterBackend::new_with_specialist(
670 Target::Ptx,
671 Box::new(MockGeneral {
672 name: "general",
673 body: "general".into(),
674 }),
675 Box::new(MockSpecialist {
676 name: "specialist",
677 matches: true,
678 body: "specialist tuned".into(),
679 }),
680 QuorumPolicy::PreferSpecialist,
681 );
682 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
683 assert_eq!(artifact.primary, "specialist tuned");
684 assert_eq!(
685 artifact.quorum_status,
686 QuorumStatus::Single {
687 emitter: "specialist".to_string()
688 }
689 );
690 }
691
692 #[test]
693 fn multi_emitter_strict_match_records_zero_diff() {
694 let backend = MultiEmitterBackend::new_with_specialist(
695 Target::Ptx,
696 Box::new(MockGeneral {
697 name: "general",
698 body: "same output".into(),
699 }),
700 Box::new(MockSpecialist {
701 name: "specialist",
702 matches: true,
703 body: "same output".into(),
704 }),
705 QuorumPolicy::Strict,
706 );
707 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
708 match artifact.quorum_status {
709 QuorumStatus::Multi {
710 emitters,
711 diff_exec,
712 } => {
713 assert_eq!(emitters, vec!["general", "specialist"]);
714 assert_eq!(diff_exec, Some(DiffExecResult::Match { max_abs_diff: 0.0 }));
715 }
716 _ => panic!("expected Multi quorum status"),
717 }
718 assert_eq!(artifact.sidecars.len(), 1);
720 assert_eq!(artifact.sidecars[0].0, "specialist_emission");
721 }
722
723 #[test]
724 fn multi_emitter_strict_divergence_records_infinity() {
725 let backend = MultiEmitterBackend::new_with_specialist(
726 Target::Ptx,
727 Box::new(MockGeneral {
728 name: "general",
729 body: "general output".into(),
730 }),
731 Box::new(MockSpecialist {
732 name: "specialist",
733 matches: true,
734 body: "different output".into(),
735 }),
736 QuorumPolicy::Strict,
737 );
738 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
739 match artifact.quorum_status {
740 QuorumStatus::Multi { diff_exec, .. } => {
741 assert!(matches!(diff_exec, Some(DiffExecResult::Divergent { .. })));
742 }
743 _ => panic!("expected Multi quorum status"),
744 }
745 }
746
747 #[test]
748 fn multi_emitter_diff_exec_records_not_run_until_engine_plugged_in() {
749 let backend = MultiEmitterBackend::new_with_specialist(
750 Target::Ptx,
751 Box::new(MockGeneral {
752 name: "rustc_codegen_nvvm",
753 body: "ptx general".into(),
754 }),
755 Box::new(MockSpecialist {
756 name: "aprender-gpu",
757 matches: true,
758 body: "ptx specialist".into(),
759 }),
760 QuorumPolicy::DiffExec { tolerance: 1e-3 },
761 );
762 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
763 match artifact.quorum_status {
764 QuorumStatus::Multi {
765 emitters,
766 diff_exec,
767 } => {
768 assert_eq!(emitters, vec!["rustc_codegen_nvvm", "aprender-gpu"]);
769 assert!(matches!(diff_exec, Some(DiffExecResult::NotRun { .. })));
771 }
772 _ => panic!("expected Multi quorum status"),
773 }
774 }
775
776 struct MockGeneralWithCitations {
787 body: String,
788 citations: Vec<ContractId>,
789 }
790 impl TargetEmitter for MockGeneralWithCitations {
791 fn name(&self) -> &str {
792 "general-with-cites"
793 }
794 fn try_emit(
795 &self,
796 _module: &Module,
797 _config: &BackendConfig,
798 ) -> Option<Result<EmittedText, BackendError>> {
799 Some(Ok(EmittedText {
800 primary: self.body.clone(),
801 citations: self.citations.clone(),
802 }))
803 }
804 }
805
806 struct MockSpecialistWithCitations {
809 body: String,
810 citations: Vec<ContractId>,
811 }
812 impl TargetEmitter for MockSpecialistWithCitations {
813 fn name(&self) -> &str {
814 "specialist-with-cites"
815 }
816 fn try_emit(
817 &self,
818 _module: &Module,
819 _config: &BackendConfig,
820 ) -> Option<Result<EmittedText, BackendError>> {
821 Some(Ok(EmittedText {
822 primary: self.body.clone(),
823 citations: self.citations.clone(),
824 }))
825 }
826 }
827
828 struct MockFailingEmitter {
831 name: &'static str,
832 err: String,
833 }
834 impl TargetEmitter for MockFailingEmitter {
835 fn name(&self) -> &str {
836 self.name
837 }
838 fn try_emit(
839 &self,
840 _module: &Module,
841 _config: &BackendConfig,
842 ) -> Option<Result<EmittedText, BackendError>> {
843 Some(Err(BackendError::Lower(self.err.clone())))
844 }
845 }
846
847 struct MockNoneEmitter;
851 impl TargetEmitter for MockNoneEmitter {
852 fn name(&self) -> &str {
853 "always-none"
854 }
855 fn try_emit(
856 &self,
857 _module: &Module,
858 _config: &BackendConfig,
859 ) -> Option<Result<EmittedText, BackendError>> {
860 None
861 }
862 }
863
864 #[test]
865 fn strict_divergence_preserves_general_citations_not_specialist() {
866 let backend = MultiEmitterBackend::new_with_specialist(
872 Target::Ptx,
873 Box::new(MockGeneralWithCitations {
874 body: "general output".into(),
875 citations: vec![ContractId::new("C-GENERAL-CITED")],
876 }),
877 Box::new(MockSpecialistWithCitations {
878 body: "different output".into(),
879 citations: vec![ContractId::new("C-SPECIALIST-CITED")],
880 }),
881 QuorumPolicy::Strict,
882 );
883 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
884 assert_eq!(artifact.citations.len(), 1);
885 assert_eq!(artifact.citations[0].as_str(), "C-GENERAL-CITED");
886 assert_eq!(artifact.sidecars.len(), 1);
889 assert_eq!(
890 artifact.sidecars[0].1,
891 b"different output".to_vec(),
892 "specialist body should be preserved in sidecar"
893 );
894 }
895
896 #[test]
897 fn prefer_specialist_hides_divergence_by_design() {
898 let backend = MultiEmitterBackend::new_with_specialist(
905 Target::Ptx,
906 Box::new(MockGeneralWithCitations {
907 body: "general thinks the answer is 42".into(),
908 citations: vec![ContractId::new("C-GENERAL")],
909 }),
910 Box::new(MockSpecialistWithCitations {
911 body: "specialist thinks the answer is 99".into(),
912 citations: vec![ContractId::new("C-SPECIALIST")],
913 }),
914 QuorumPolicy::PreferSpecialist,
915 );
916 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
917 assert!(artifact.primary.contains("99"));
920 assert_eq!(artifact.citations[0].as_str(), "C-SPECIALIST");
921 match artifact.quorum_status {
922 QuorumStatus::Single { emitter } => {
923 assert_eq!(emitter, "specialist-with-cites");
924 }
925 other => panic!("expected Single quorum status, got {other:?}"),
926 }
927 assert!(artifact.sidecars.is_empty());
929 }
930
931 #[test]
932 fn general_emitter_failure_propagates() {
933 let backend = MultiEmitterBackend::new_with_specialist(
938 Target::Ptx,
939 Box::new(MockFailingEmitter {
940 name: "general-broken",
941 err: "general blew up".into(),
942 }),
943 Box::new(MockSpecialist {
944 name: "specialist",
945 matches: true,
946 body: "specialist output".into(),
947 }),
948 QuorumPolicy::PreferSpecialist,
949 );
950 let err = backend.lower(&dummy_module(), &dummy_config()).unwrap_err();
951 match err {
952 BackendError::Lower(msg) => assert!(msg.contains("general blew up")),
953 other => panic!("expected Lower error, got {other:?}"),
954 }
955 }
956
957 #[test]
958 fn specialist_emitter_failure_propagates_when_matched() {
959 let backend = MultiEmitterBackend::new_with_specialist(
963 Target::Ptx,
964 Box::new(MockGeneral {
965 name: "general",
966 body: "general output".into(),
967 }),
968 Box::new(MockFailingEmitter {
969 name: "specialist-broken",
970 err: "specialist blew up after matching".into(),
971 }),
972 QuorumPolicy::Strict,
973 );
974 let err = backend.lower(&dummy_module(), &dummy_config()).unwrap_err();
975 match err {
976 BackendError::Lower(msg) => {
977 assert!(msg.contains("specialist blew up after matching"))
978 }
979 other => panic!("expected Lower error, got {other:?}"),
980 }
981 }
982
983 #[test]
984 fn general_returning_none_is_a_hard_contract_violation() {
985 let backend = MultiEmitterBackend::new_single(Target::Ptx, Box::new(MockNoneEmitter));
989 let err = backend.lower(&dummy_module(), &dummy_config()).unwrap_err();
990 match err {
991 BackendError::Lower(msg) => {
992 assert!(
993 msg.contains("always-none"),
994 "error should name the offending emitter; got: {msg}"
995 );
996 assert!(msg.contains("must always match"));
997 }
998 other => panic!("expected Lower error, got {other:?}"),
999 }
1000 }
1001
1002 #[test]
1003 fn diff_exec_not_run_reason_records_tolerance_for_observability() {
1004 let backend = MultiEmitterBackend::new_with_specialist(
1008 Target::Ptx,
1009 Box::new(MockGeneral {
1010 name: "general",
1011 body: "g".into(),
1012 }),
1013 Box::new(MockSpecialist {
1014 name: "specialist",
1015 matches: true,
1016 body: "s".into(),
1017 }),
1018 QuorumPolicy::DiffExec { tolerance: 2.5e-4 },
1019 );
1020 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
1021 match artifact.quorum_status {
1022 QuorumStatus::Multi {
1023 diff_exec: Some(DiffExecResult::NotRun { reason }),
1024 ..
1025 } => {
1026 assert!(
1027 reason.contains("0.00025")
1028 || reason.contains("2.5e-4")
1029 || reason.contains("0.000250"),
1030 "tolerance should appear in NotRun reason; got: {reason}"
1031 );
1032 }
1033 other => panic!("expected Multi NotRun status, got {other:?}"),
1034 }
1035 }
1036
1037 #[test]
1038 fn diff_exec_does_not_short_circuit_on_text_equality() {
1039 let backend = MultiEmitterBackend::new_with_specialist(
1048 Target::Ptx,
1049 Box::new(MockGeneral {
1050 name: "general",
1051 body: "byte identical".into(),
1052 }),
1053 Box::new(MockSpecialist {
1054 name: "specialist",
1055 matches: true,
1056 body: "byte identical".into(),
1057 }),
1058 QuorumPolicy::DiffExec { tolerance: 1e-6 },
1059 );
1060 let artifact = backend.lower(&dummy_module(), &dummy_config()).unwrap();
1061 match artifact.quorum_status {
1062 QuorumStatus::Multi { diff_exec, .. } => {
1063 assert!(
1064 matches!(diff_exec, Some(DiffExecResult::NotRun { .. })),
1065 "DiffExec policy must NOT short-circuit on text equality \
1066 — engine compares runtime values, not source text"
1067 );
1068 }
1069 other => panic!("expected Multi quorum status, got {other:?}"),
1070 }
1071 }
1072}