use crate::query::json::J;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NamedMismatch {
pub id: &'static str,
pub attributed_producer: &'static str,
pub reason: &'static str,
pub evidence: &'static str,
}
pub const RED_BASELINE: &[NamedMismatch] = &[
NamedMismatch {
id: "unrelated-names-promoted-to-framework-facts",
attributed_producer: "scout-semantic FactsWalker sidecar",
reason: "ordinary, unrelated methods named Publish/AddScoped<T,U>/MapGet on a type with no \
messaging or DI dependency are promoted to publish/di_binding/route facts",
evidence: "fixtures/csharp-truth/src/UnrelatedApiNames.cs",
},
NamedMismatch {
id: "directory-move-creates-message-class-fact",
attributed_producer: "scout-semantic FactsWalker sidecar",
reason: "moving an unchanged class from a plain directory into a messaging-named directory \
creates a message_class fact with no source change to the class itself",
evidence: "fixtures/csharp-truth/src/DirectoryMove",
},
NamedMismatch {
id: "failed-binding-promoted-while-unit-reports-healthy",
attributed_producer: "scout-semantic FactsWalker sidecar",
reason: "a call through an undeclared receiver still emits a publish fact while the owning \
unit's own status is reported ok",
evidence: "fixtures/csharp-truth/src/FailedBinding.cs",
},
NamedMismatch {
id: "same-line-overloads-collapse-to-one-reference",
attributed_producer: "scout-semantic Roslyn oracle exporter",
reason: "two distinct overloads called on one line deduplicate to a single ground-truth \
reference, so the two occurrences and their distinct overload identities cannot be \
told apart by anything downstream that joins on the oracle's own record",
evidence: "fixtures/csharp-truth/src/Overloads.cs",
},
NamedMismatch {
id: "type-argument-pair-produces-implements-edges-on-a-clean-build",
attributed_producer: "devscout native dispatch resolution",
reason: "an unrelated, unconstrained generic method whose name matches the Add*/*Scoped \
registration convention produces type-level and member-level implements edges \
against an interface it never implements, on a project that builds cleanly",
evidence: "fixtures/csharp-truth/src/NativeDispatchCounterexample.cs",
},
];
pub const GRADED_THROUGH_REAL_PRODUCER_OUTPUT: &[&str] =
&["type-argument-pair-produces-implements-edges-on-a-clean-build"];
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HarnessHealth {
Ok,
Broken {
reasons: Vec<String>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnalyzerVerdict {
Green,
Red {
mismatch_ids: Vec<&'static str>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BaselineRun {
pub harness_health: HarnessHealth,
pub analyzer_verdict: AnalyzerVerdict,
}
pub fn classify_mismatches(
observed_ids: &[&'static str],
) -> (Vec<&'static str>, Vec<&'static str>) {
let known: Vec<&'static str> = RED_BASELINE.iter().map(|m| m.id).collect();
let mut named = Vec::new();
let mut new = Vec::new();
for id in observed_ids {
if known.contains(id) {
named.push(*id);
} else {
new.push(*id);
}
}
(named, new)
}
pub fn evaluate_run(
harness_tests_passed: bool,
observed_mismatch_ids: &[&'static str],
) -> BaselineRun {
let harness_health = if harness_tests_passed {
HarnessHealth::Ok
} else {
HarnessHealth::Broken {
reasons: vec![
"a harness self-test failed independently of the analyzer verdict".to_string(),
],
}
};
let analyzer_verdict = if observed_mismatch_ids.is_empty() {
AnalyzerVerdict::Green
} else {
AnalyzerVerdict::Red {
mismatch_ids: observed_mismatch_ids.to_vec(),
}
};
BaselineRun {
harness_health,
analyzer_verdict,
}
}
pub fn red_baseline_to_json() -> String {
J::Obj(vec![
("contract", J::Str("semantic-truth-v1".to_string())),
(
"mismatches",
J::Arr(
RED_BASELINE
.iter()
.map(|m| {
J::Obj(vec![
("id", J::Str(m.id.to_string())),
(
"attributedProducer",
J::Str(m.attributed_producer.to_string()),
),
("reason", J::Str(m.reason.to_string())),
("evidence", J::Str(m.evidence.to_string())),
])
})
.collect(),
),
),
])
.to_json_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_red_analyzer_verdict_does_not_imply_a_broken_harness() {
let run = evaluate_run(true, &["unrelated-names-promoted-to-framework-facts"]);
assert_eq!(run.harness_health, HarnessHealth::Ok);
assert!(matches!(run.analyzer_verdict, AnalyzerVerdict::Red { .. }));
}
#[test]
fn a_green_run_with_no_observed_mismatch() {
let run = evaluate_run(true, &[]);
assert_eq!(run.analyzer_verdict, AnalyzerVerdict::Green);
}
#[test]
fn every_registered_row_names_its_attributed_producer() {
for row in RED_BASELINE {
assert!(!row.attributed_producer.is_empty());
assert!(!row.reason.is_empty());
}
assert_eq!(RED_BASELINE.len(), 5);
}
#[test]
fn an_id_the_baseline_does_not_name_is_reported_as_new() {
let (named, new) = classify_mismatches(&[
"unrelated-names-promoted-to-framework-facts",
"never-seen-before",
]);
assert_eq!(named, vec!["unrelated-names-promoted-to-framework-facts"]);
assert_eq!(new, vec!["never-seen-before"]);
}
#[test]
fn baseline_json_is_byte_identical_across_two_builds() {
assert_eq!(red_baseline_to_json(), red_baseline_to_json());
}
#[test]
fn every_id_graded_through_real_producer_output_is_a_registered_row() {
for id in GRADED_THROUGH_REAL_PRODUCER_OUTPUT {
assert!(
RED_BASELINE.iter().any(|m| &m.id == id),
"'{id}' is graded but not a registered row"
);
}
}
}