use std::collections::BTreeMap;
use serde_json::Value;
use crate::analysis::ClassifiedSeam;
use crate::analysis::canonical_gap::{CanonicalGapIdentity, canonical_gap_identities};
use crate::analysis::seams::SeamGripClass;
use crate::domain::{OracleKind, OracleStrength, StageState};
use crate::output::evidence_record::{EvidenceRecord, evidence_record_for};
pub(crate) const EVIDENCE_HEALTH_SCHEMA_VERSION: &str = "0.1";
const EVIDENCE_HEALTH_TOP_GROUP_LIMIT: usize = 10;
const EVIDENCE_HEALTH_TOP_RISK_LIMIT: usize = 5;
const STAGE_LABELS: &[&str] = &["yes", "weak", "no", "unknown", "opaque", "not_applicable"];
const ORACLE_STRENGTH_LABELS: &[&str] = &["strong", "medium", "weak", "smoke", "none", "unknown"];
const ORACLE_KIND_LABELS: &[&str] = &[
"exact_value",
"exact_error_variant",
"whole_object_equality",
"snapshot",
"relational_check",
"broad_error",
"smoke_only",
"mock_expectation",
"unknown",
];
const RELATION_CONFIDENCE_LABELS: &[&str] = &["high", "medium", "low", "opaque"];
const VALUE_CONTEXT_LABELS: &[&str] = &[
"function_argument",
"assertion_argument",
"builder_method",
"table_row",
"enum_variant",
"return_value",
"unknown",
];
mod json;
mod markdown;
pub(crate) use json::render_evidence_health_json;
pub(crate) use markdown::render_evidence_health_markdown;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct EvidenceHealthReport {
root: String,
metrics: EvidenceHealthMetrics,
evidence_quality: EvidenceHealthQuality,
calibration: EvidenceHealthCalibration,
top_static_limitations: Vec<StaticLimitation>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct EvidenceHealthMetrics {
seams_total: usize,
headline_eligible_total: usize,
weakly_gripped_total: usize,
ungripped_total: usize,
grip_class_counts: BTreeMap<String, usize>,
stage_state_counts: BTreeMap<String, BTreeMap<String, usize>>,
unknown_stage_counts: BTreeMap<String, usize>,
unknown_stop_reason_counts: BTreeMap<String, usize>,
missing_discriminators_total: usize,
seams_with_missing_discriminators: usize,
missing_discriminator_counts: BTreeMap<String, usize>,
observed_values_total: usize,
seams_with_observed_values: usize,
observed_value_context_counts: BTreeMap<String, usize>,
related_tests_total: usize,
seams_with_related_tests: usize,
related_test_confidence_counts: BTreeMap<String, usize>,
oracle_strength_counts: BTreeMap<String, usize>,
oracle_kind_counts: BTreeMap<String, usize>,
opaque_oracle_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct EvidenceHealthQuality {
canonical_gap_groups_total: usize,
duplicate_looking_groups_total: usize,
largest_canonical_groups: Vec<EvidenceHealthCanonicalGroup>,
actionability_class_counts: BTreeMap<String, usize>,
static_limitation_stage_counts: BTreeMap<String, usize>,
static_limitation_reason_counts: BTreeMap<String, usize>,
static_limitation_category_counts: BTreeMap<String, usize>,
calibration_availability_counts: BTreeMap<String, usize>,
movement_availability: EvidenceHealthMovementAvailability,
top_evidence_quality_risks: Vec<EvidenceHealthRisk>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct EvidenceHealthMovementAvailability {
records_with_seam_id: usize,
records_with_canonical_gap_id: usize,
records_with_complete_evidence_path: usize,
records_with_recommendation: usize,
records_with_verify_command: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct EvidenceHealthCanonicalGroup {
canonical_gap_id: String,
count: usize,
reported_group_size: Option<usize>,
owner: String,
seam_kind: String,
flow_sink: String,
missing_discriminator: String,
assertion_shape: String,
example_seam_id: String,
example_file: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct EvidenceHealthCanonicalGroupCounter {
count: usize,
reported_group_size: Option<usize>,
owner: String,
seam_kind: String,
flow_sink: String,
missing_discriminator: String,
assertion_shape: String,
example_seam_id: String,
example_file: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct EvidenceHealthRisk {
kind: String,
count: usize,
summary: String,
}
#[derive(Debug, Default)]
struct EvidenceHealthQualityCounters {
canonical_group_counts: BTreeMap<String, EvidenceHealthCanonicalGroupCounter>,
actionability_class_counts: BTreeMap<String, usize>,
static_limitation_stage_counts: BTreeMap<String, usize>,
static_limitation_reason_counts: BTreeMap<String, usize>,
static_limitation_category_counts: BTreeMap<String, usize>,
calibration_availability_counts: BTreeMap<String, usize>,
movement_availability: EvidenceHealthMovementAvailability,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct EvidenceHealthCalibration {
status: String,
source: Option<String>,
matched_total: usize,
static_without_runtime_total: usize,
runtime_without_static_total: usize,
ambiguous_file_line_total: usize,
unmatched_runtime_total: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct StaticLimitationCounter {
count: usize,
summary: String,
example_seam_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct StaticLimitation {
kind: String,
count: usize,
summary: String,
example_seam_id: Option<String>,
}
impl EvidenceHealthCalibration {
pub(crate) fn not_provided() -> Self {
Self {
status: "not_provided".to_string(),
source: None,
matched_total: 0,
static_without_runtime_total: 0,
runtime_without_static_total: 0,
ambiguous_file_line_total: 0,
unmatched_runtime_total: 0,
}
}
pub(crate) fn from_json(source: String, contents: &str) -> Result<Self, String> {
let value: Value = serde_json::from_str(contents)
.map_err(|err| format!("failed to parse mutation calibration context: {err}"))?;
Ok(Self {
status: "loaded".to_string(),
source: Some(source),
matched_total: usize_field(&value, &["summary", "matched_total"]),
static_without_runtime_total: usize_field(
&value,
&["summary", "static_without_runtime_total"],
),
runtime_without_static_total: value
.get("missed_runtime_signals")
.and_then(Value::as_array)
.map_or(0, Vec::len),
ambiguous_file_line_total: usize_field(
&value,
&["summary", "ambiguous_file_line_total"],
),
unmatched_runtime_total: usize_field(&value, &["summary", "unmatched_mutants_total"]),
})
}
}
pub(crate) fn build_evidence_health_report(
classified: &[ClassifiedSeam],
root: String,
calibration: EvidenceHealthCalibration,
) -> EvidenceHealthReport {
let mut grip_class_counts = grip_class_counts();
let mut stage_state_counts = stage_state_counts();
let mut unknown_stage_counts = stage_counts();
let mut unknown_stop_reason_counts = unknown_stop_reason_counts();
let mut missing_discriminator_counts = BTreeMap::new();
let mut observed_value_context_counts = labeled_counts(VALUE_CONTEXT_LABELS);
let mut related_test_confidence_counts = labeled_counts(RELATION_CONFIDENCE_LABELS);
let mut oracle_strength_counts = labeled_counts(ORACLE_STRENGTH_LABELS);
let mut oracle_kind_counts = labeled_counts(ORACLE_KIND_LABELS);
let mut limitations: BTreeMap<String, StaticLimitationCounter> = BTreeMap::new();
let canonical_gaps = canonical_gap_identities(classified);
let mut quality_counters = EvidenceHealthQualityCounters::default();
let mut headline_eligible_total = 0;
let mut missing_discriminators_total = 0;
let mut seams_with_missing_discriminators = 0;
let mut observed_values_total = 0;
let mut seams_with_observed_values = 0;
let mut related_tests_total = 0;
let mut seams_with_related_tests = 0;
let mut opaque_oracle_count = 0;
for entry in classified {
let canonical_gap = canonical_gaps.get(entry.seam.id());
let record = evidence_record_for(entry, canonical_gap);
count_evidence_record_quality(&record, canonical_gap, &mut quality_counters);
increment(&mut grip_class_counts, entry.class.as_str());
if entry.class.is_headline_eligible() {
headline_eligible_total += 1;
}
increment_unknown_stop_reason(&mut unknown_stop_reason_counts, entry.class);
count_stage(
&mut stage_state_counts,
&mut unknown_stage_counts,
&mut limitations,
"reach",
&entry.evidence.reach.state,
entry.seam.id().as_str(),
);
count_stage(
&mut stage_state_counts,
&mut unknown_stage_counts,
&mut limitations,
"activate",
&entry.evidence.activate.state,
entry.seam.id().as_str(),
);
count_stage(
&mut stage_state_counts,
&mut unknown_stage_counts,
&mut limitations,
"propagate",
&entry.evidence.propagate.state,
entry.seam.id().as_str(),
);
count_stage(
&mut stage_state_counts,
&mut unknown_stage_counts,
&mut limitations,
"observe",
&entry.evidence.observe.state,
entry.seam.id().as_str(),
);
count_stage(
&mut stage_state_counts,
&mut unknown_stage_counts,
&mut limitations,
"discriminate",
&entry.evidence.discriminate.state,
entry.seam.id().as_str(),
);
if entry.evidence.related_tests.is_empty() {
increment_limitation(
&mut limitations,
"no_related_tests",
"No related test was associated with the seam.",
entry.seam.id().as_str(),
);
} else {
seams_with_related_tests += 1;
}
if !entry.evidence.observed_values.is_empty() {
seams_with_observed_values += 1;
}
observed_values_total += entry.evidence.observed_values.len();
for value in &entry.evidence.observed_values {
increment(&mut observed_value_context_counts, value.context.as_str());
}
if !entry.evidence.missing_discriminators.is_empty() {
seams_with_missing_discriminators += 1;
increment_limitation(
&mut limitations,
"missing_discriminator",
"At least one discriminator remains missing for the seam.",
entry.seam.id().as_str(),
);
}
missing_discriminators_total += entry.evidence.missing_discriminators.len();
for missing in &entry.evidence.missing_discriminators {
increment(&mut missing_discriminator_counts, missing.value.as_str());
}
related_tests_total += entry.evidence.related_tests.len();
for related in &entry.evidence.related_tests {
increment(
&mut related_test_confidence_counts,
related.relation_confidence.as_str(),
);
increment(
&mut oracle_strength_counts,
related.oracle_strength.as_str(),
);
increment(&mut oracle_kind_counts, related.oracle_kind.as_str());
if related.oracle_kind == OracleKind::Unknown
|| related.oracle_strength == OracleStrength::Unknown
{
opaque_oracle_count += 1;
increment_limitation(
&mut limitations,
"opaque_oracle",
"A related test contains an assertion shape ripr cannot classify.",
entry.seam.id().as_str(),
);
}
}
}
let metrics = EvidenceHealthMetrics {
seams_total: classified.len(),
headline_eligible_total,
weakly_gripped_total: count_for(&grip_class_counts, "weakly_gripped"),
ungripped_total: count_for(&grip_class_counts, "ungripped"),
grip_class_counts,
stage_state_counts,
unknown_stage_counts,
unknown_stop_reason_counts,
missing_discriminators_total,
seams_with_missing_discriminators,
missing_discriminator_counts,
observed_values_total,
seams_with_observed_values,
observed_value_context_counts,
related_tests_total,
seams_with_related_tests,
related_test_confidence_counts,
oracle_strength_counts,
oracle_kind_counts,
opaque_oracle_count,
};
let evidence_quality = evidence_quality_from_counts(quality_counters, &metrics);
EvidenceHealthReport {
root,
metrics,
evidence_quality,
calibration,
top_static_limitations: top_limitations(limitations),
}
}
fn grip_class_counts() -> BTreeMap<String, usize> {
let mut counts = BTreeMap::new();
for class in SeamGripClass::ALL {
counts.insert(class.as_str().to_string(), 0);
}
counts
}
fn stage_state_counts() -> BTreeMap<String, BTreeMap<String, usize>> {
let mut counts = BTreeMap::new();
for stage in ["reach", "activate", "propagate", "observe", "discriminate"] {
counts.insert(stage.to_string(), labeled_counts(STAGE_LABELS));
}
counts
}
fn stage_counts() -> BTreeMap<String, usize> {
let mut counts = BTreeMap::new();
for stage in ["reach", "activate", "propagate", "observe", "discriminate"] {
counts.insert(stage.to_string(), 0);
}
counts
}
fn unknown_stop_reason_counts() -> BTreeMap<String, usize> {
labeled_counts(&[
"activation_unknown",
"propagation_unknown",
"observation_unknown",
"discrimination_unknown",
"opaque",
])
}
fn labeled_counts(labels: &[&str]) -> BTreeMap<String, usize> {
labels
.iter()
.map(|label| ((*label).to_string(), 0))
.collect()
}
fn count_stage(
stage_state_counts: &mut BTreeMap<String, BTreeMap<String, usize>>,
unknown_stage_counts: &mut BTreeMap<String, usize>,
limitations: &mut BTreeMap<String, StaticLimitationCounter>,
stage: &str,
state: &StageState,
seam_id: &str,
) {
if let Some(counts) = stage_state_counts.get_mut(stage) {
increment(counts, state.as_str());
}
if matches!(state, StageState::Unknown | StageState::Opaque) {
increment(unknown_stage_counts, stage);
let kind = format!("{stage}_unknown");
let summary = format!("The {stage} stage is unknown or opaque for at least one seam.");
increment_limitation(limitations, &kind, &summary, seam_id);
}
}
fn increment_unknown_stop_reason(counts: &mut BTreeMap<String, usize>, class: SeamGripClass) {
let key = match class {
SeamGripClass::ActivationUnknown => Some("activation_unknown"),
SeamGripClass::PropagationUnknown => Some("propagation_unknown"),
SeamGripClass::ObservationUnknown => Some("observation_unknown"),
SeamGripClass::DiscriminationUnknown => Some("discrimination_unknown"),
SeamGripClass::Opaque => Some("opaque"),
_ => None,
};
if let Some(key) = key {
increment(counts, key);
}
}
fn increment(map: &mut BTreeMap<String, usize>, key: &str) {
let entry = map.entry(key.to_string()).or_insert(0);
*entry += 1;
}
fn increment_limitation(
limitations: &mut BTreeMap<String, StaticLimitationCounter>,
kind: &str,
summary: &str,
seam_id: &str,
) {
let entry = limitations
.entry(kind.to_string())
.or_insert_with(|| StaticLimitationCounter {
count: 0,
summary: summary.to_string(),
example_seam_id: None,
});
entry.count += 1;
if entry.example_seam_id.is_none() {
entry.example_seam_id = Some(seam_id.to_string());
}
}
fn top_limitations(
limitations: BTreeMap<String, StaticLimitationCounter>,
) -> Vec<StaticLimitation> {
let mut rows: Vec<_> = limitations
.into_iter()
.map(|(kind, counter)| StaticLimitation {
kind,
count: counter.count,
summary: counter.summary,
example_seam_id: counter.example_seam_id,
})
.collect();
rows.sort_by(|left, right| {
right
.count
.cmp(&left.count)
.then_with(|| left.kind.cmp(&right.kind))
});
rows.truncate(10);
rows
}
fn count_for(counts: &BTreeMap<String, usize>, key: &str) -> usize {
counts.get(key).copied().unwrap_or(0)
}
fn count_evidence_record_quality(
record: &EvidenceRecord,
canonical_gap: Option<&CanonicalGapIdentity>,
counters: &mut EvidenceHealthQualityCounters,
) {
if let Some(gap) = canonical_gap {
let entry = counters
.canonical_group_counts
.entry(gap.id.clone())
.or_insert_with(|| EvidenceHealthCanonicalGroupCounter {
count: 0,
reported_group_size: record.canonical_gap_group_size,
owner: gap.owner.clone(),
seam_kind: gap.seam_kind.clone(),
flow_sink: gap.flow_sink.clone(),
missing_discriminator: gap.missing_discriminator.clone(),
assertion_shape: gap.assertion_shape.clone(),
example_seam_id: record.seam_id.clone(),
example_file: record.location.file.clone(),
});
entry.count += 1;
entry.reported_group_size = record.canonical_gap_group_size;
}
increment(
&mut counters.actionability_class_counts,
&record.actionability.class,
);
increment(
&mut counters.calibration_availability_counts,
&record.calibration.availability,
);
if !record.seam_id.is_empty() {
counters.movement_availability.records_with_seam_id += 1;
}
if record.canonical_gap_id.is_some() {
counters.movement_availability.records_with_canonical_gap_id += 1;
}
if evidence_path_is_complete(record) {
counters
.movement_availability
.records_with_complete_evidence_path += 1;
}
if !record.recommendation.action.is_empty() {
counters.movement_availability.records_with_recommendation += 1;
}
if record.recommendation.verify_command.is_some() {
counters.movement_availability.records_with_verify_command += 1;
}
for limitation in &record.static_limitations {
increment(
&mut counters.static_limitation_stage_counts,
&limitation.stage,
);
increment(
&mut counters.static_limitation_reason_counts,
&limitation.reason,
);
increment(
&mut counters.static_limitation_category_counts,
&limitation.category,
);
}
}
fn evidence_path_is_complete(record: &EvidenceRecord) -> bool {
[
&record.evidence_path.reach,
&record.evidence_path.activate,
&record.evidence_path.propagate,
&record.evidence_path.observe,
&record.evidence_path.discriminate,
]
.into_iter()
.all(|stage| !stage.state.is_empty() && !stage.confidence.is_empty())
}
fn evidence_quality_from_counts(
counters: EvidenceHealthQualityCounters,
metrics: &EvidenceHealthMetrics,
) -> EvidenceHealthQuality {
let mut groups = counters
.canonical_group_counts
.into_iter()
.map(|(canonical_gap_id, counter)| EvidenceHealthCanonicalGroup {
canonical_gap_id,
count: counter.count,
reported_group_size: counter.reported_group_size,
owner: counter.owner,
seam_kind: counter.seam_kind,
flow_sink: counter.flow_sink,
missing_discriminator: counter.missing_discriminator,
assertion_shape: counter.assertion_shape,
example_seam_id: counter.example_seam_id,
example_file: counter.example_file,
})
.collect::<Vec<_>>();
groups.sort_by(|left, right| {
right
.count
.cmp(&left.count)
.then_with(|| left.canonical_gap_id.cmp(&right.canonical_gap_id))
});
let canonical_gap_groups_total = groups.len();
let duplicate_looking_groups_total = groups.iter().filter(|group| group.count > 1).count();
groups.truncate(EVIDENCE_HEALTH_TOP_GROUP_LIMIT);
let static_limitations_total = counters.static_limitation_reason_counts.values().sum();
let uncalibrated_records = count_for(&counters.calibration_availability_counts, "not_imported");
let low_or_opaque_related_tests = count_for(&metrics.related_test_confidence_counts, "low")
+ count_for(&metrics.related_test_confidence_counts, "opaque");
let mut risks = Vec::new();
push_risk(
&mut risks,
"static_limitations",
static_limitations_total,
"Evidence records still contain static limitations.",
);
push_risk(
&mut risks,
"missing_discriminators",
metrics.missing_discriminators_total,
"Headline evidence still lacks concrete discriminator coverage.",
);
push_risk(
&mut risks,
"uncalibrated_records",
uncalibrated_records,
"Evidence records do not have imported runtime calibration data.",
);
push_risk(
&mut risks,
"duplicate_canonical_groups",
duplicate_looking_groups_total,
"Canonical gap groups still contain more than one raw seam.",
);
push_risk(
&mut risks,
"low_or_opaque_related_tests",
low_or_opaque_related_tests,
"Related-test evidence includes low-confidence or opaque rankings.",
);
risks.sort_by(|left, right| {
right
.count
.cmp(&left.count)
.then_with(|| left.kind.cmp(&right.kind))
});
risks.truncate(EVIDENCE_HEALTH_TOP_RISK_LIMIT);
EvidenceHealthQuality {
canonical_gap_groups_total,
duplicate_looking_groups_total,
largest_canonical_groups: groups,
actionability_class_counts: counters.actionability_class_counts,
static_limitation_stage_counts: counters.static_limitation_stage_counts,
static_limitation_reason_counts: counters.static_limitation_reason_counts,
static_limitation_category_counts: counters.static_limitation_category_counts,
calibration_availability_counts: counters.calibration_availability_counts,
movement_availability: counters.movement_availability,
top_evidence_quality_risks: risks,
}
}
fn push_risk(risks: &mut Vec<EvidenceHealthRisk>, kind: &str, count: usize, summary: &str) {
if count == 0 {
return;
}
risks.push(EvidenceHealthRisk {
kind: kind.to_string(),
count,
summary: summary.to_string(),
});
}
fn usize_field(value: &Value, path: &[&str]) -> usize {
let mut current = value;
for segment in path {
match current.get(*segment) {
Some(next) => current = next,
None => return 0,
}
}
current.as_u64().map_or(0, |number| number as usize)
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use serde_json::Value;
use super::*;
use crate::analysis::seams::{ExpectedSink, RepoSeam, RequiredDiscriminator, SeamKind};
use crate::analysis::test_grip_evidence::{
RelatedTestGrip, RelationConfidence, RelationReason, TestGripEvidence,
};
use crate::domain::{
Confidence, MissingDiscriminatorFact, StageEvidence, StageState, ValueContext, ValueFact,
};
#[test]
fn evidence_health_counts_core_metrics() -> Result<(), String> {
let report = build_evidence_health_report(
&[
weak_boundary_seam(),
weak_boundary_seam_at_line(121),
opaque_call_seam(),
],
".".to_string(),
EvidenceHealthCalibration::not_provided(),
);
let json = render_evidence_health_json(&report)?;
let value: Value = serde_json::from_str(&json).map_err(|err| err.to_string())?;
assert_eq!(value["schema_version"], Value::from("0.1"));
assert_eq!(value["metrics"]["seams_total"], Value::from(3));
assert_eq!(value["metrics"]["weakly_gripped_total"], Value::from(2));
assert_eq!(value["metrics"]["ungripped_total"], Value::from(1));
assert_eq!(
value["metrics"]["missing_discriminators_total"],
Value::from(2)
);
assert_eq!(value["metrics"]["observed_values_total"], Value::from(2));
assert_eq!(value["metrics"]["related_tests_total"], Value::from(3));
assert_eq!(value["metrics"]["opaque_oracle_count"], Value::from(1));
assert_eq!(
value["metrics"]["related_test_confidence_counts"]["high"],
Value::from(2)
);
assert_eq!(
value["metrics"]["oracle_strength_counts"]["unknown"],
Value::from(1)
);
assert_eq!(
value["evidence_quality"]["canonical_gap_groups_total"],
Value::from(2)
);
assert_eq!(
value["evidence_quality"]["duplicate_looking_groups_total"],
Value::from(1)
);
assert_eq!(
value["evidence_quality"]["largest_canonical_groups"][0]["count"],
Value::from(2)
);
assert_eq!(
value["evidence_quality"]["movement_availability"]["records_with_canonical_gap_id"],
Value::from(3)
);
assert_eq!(
value["evidence_quality"]["actionability_class_counts"]["actionable_related_test_extension"],
Value::from(2)
);
assert_eq!(
value["evidence_quality"]["calibration_availability_counts"]["not_imported"],
Value::from(3)
);
assert_eq!(
value["evidence_quality"]["static_limitation_category_counts"]["activation_static_unknown"],
Value::from(1)
);
Ok(())
}
#[test]
fn evidence_health_markdown_names_calibration_and_limitations() -> Result<(), String> {
let report = build_evidence_health_report(
&[weak_boundary_seam(), opaque_call_seam()],
".".to_string(),
EvidenceHealthCalibration::from_json(
"target/ripr/reports/mutation-calibration.json".to_string(),
r#"{
"summary": {
"matched_total": 2,
"static_without_runtime_total": 1,
"ambiguous_file_line_total": 1,
"unmatched_mutants_total": 3
},
"missed_runtime_signals": [{"id": "runtime-only"}]
}"#,
)?,
);
let markdown = render_evidence_health_markdown(&report);
assert!(markdown.contains("RIPR evidence health report"));
assert!(markdown.contains("Evidence Quality"));
assert!(markdown.contains("Largest Canonical Gap Groups"));
assert!(markdown.contains("Evidence-Record Calibration Coverage"));
assert!(markdown.contains("Top Evidence Quality Risks"));
assert!(markdown.contains("Matched calibration rows"));
assert!(markdown.contains("missing_discriminator"));
assert!(markdown.contains("Categories"));
assert!(markdown.contains("activation_static_unknown"));
assert!(markdown.contains("Runtime rows without static seam"));
Ok(())
}
fn weak_boundary_seam() -> ClassifiedSeam {
weak_boundary_seam_at_line(120)
}
fn weak_boundary_seam_at_line(line: usize) -> ClassifiedSeam {
let seam = RepoSeam::new(
"src/pricing.rs",
"pricing::discount",
SeamKind::PredicateBoundary,
line,
42,
"amount >= threshold",
RequiredDiscriminator::BoundaryValue {
description: "amount == threshold".to_string(),
},
ExpectedSink::ReturnValue,
);
let seam_id = seam.id().clone();
ClassifiedSeam {
seam,
evidence: TestGripEvidence {
seam_id,
related_tests: vec![RelatedTestGrip {
test_name: "discounts_large_orders".to_string(),
file: PathBuf::from("tests/pricing.rs"),
line: 12,
oracle_kind: OracleKind::ExactValue,
oracle_strength: OracleStrength::Strong,
evidence_summary: "asserts returned discount".to_string(),
relation_reason: RelationReason::DirectOwnerCall,
relation_confidence: RelationConfidence::High,
}],
reach: StageEvidence::new(StageState::Yes, Confidence::High, "direct call"),
activate: StageEvidence::new(StageState::Yes, Confidence::High, "value observed"),
propagate: StageEvidence::new(
StageState::Weak,
Confidence::Medium,
"return reaches assertion",
),
observe: StageEvidence::new(
StageState::Weak,
Confidence::Medium,
"assertion is nearby",
),
discriminate: StageEvidence::new(
StageState::No,
Confidence::Unknown,
"boundary value missing",
),
observed_values: vec![ValueFact {
line: 12,
text: "discounted_total(150)".to_string(),
value: "150".to_string(),
context: ValueContext::FunctionArgument,
}],
missing_discriminators: vec![MissingDiscriminatorFact {
value: "amount == threshold".to_string(),
reason: "equality boundary not observed".to_string(),
flow_sink: None,
}],
},
class: SeamGripClass::WeaklyGripped,
}
}
fn opaque_call_seam() -> ClassifiedSeam {
let seam = RepoSeam::new(
"src/pricing.rs",
"pricing::discount",
SeamKind::CallPresence,
180,
55,
"apply_discount(amount)",
RequiredDiscriminator::CallSite {
target: "apply_discount".to_string(),
},
ExpectedSink::SideEffect,
);
let seam_id = seam.id().clone();
ClassifiedSeam {
seam,
evidence: TestGripEvidence {
seam_id,
related_tests: vec![RelatedTestGrip {
test_name: "discounts_smoke".to_string(),
file: PathBuf::from("tests/pricing.rs"),
line: 22,
oracle_kind: OracleKind::Unknown,
oracle_strength: OracleStrength::Unknown,
evidence_summary: "helper assertion not classified".to_string(),
relation_reason: RelationReason::SameTestFile,
relation_confidence: RelationConfidence::Opaque,
}],
reach: StageEvidence::new(StageState::Yes, Confidence::Medium, "same file"),
activate: StageEvidence::new(
StageState::Unknown,
Confidence::Unknown,
"call target not activated",
),
propagate: StageEvidence::new(
StageState::Unknown,
Confidence::Unknown,
"propagation unknown",
),
observe: StageEvidence::new(
StageState::Unknown,
Confidence::Unknown,
"oracle unknown",
),
discriminate: StageEvidence::new(
StageState::Unknown,
Confidence::Unknown,
"discriminator unknown",
),
observed_values: Vec::new(),
missing_discriminators: Vec::new(),
},
class: SeamGripClass::Ungripped,
}
}
}