use serde_json::{Value, json};
const SCHEMA_VERSION: &str = "0.1";
const REPORT_KIND: &str = "coverage_grip_frontier";
const LIMITS_NOTE: &str = "Coverage is execution evidence; RIPR is static behavioral grip evidence. This report is advisory and does not claim test adequacy or runtime mutation outcomes.";
pub(crate) const DEFAULT_COVERAGE_GRIP_FRONTIER_OUT: &str =
"target/ripr/reports/coverage-grip-frontier.json";
pub(crate) const DEFAULT_COVERAGE_GRIP_FRONTIER_MD_OUT: &str =
"target/ripr/reports/coverage-grip-frontier.md";
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct CoverageGripFrontierInput {
pub(crate) root: String,
pub(crate) generated_at: String,
pub(crate) coverage_path: Option<String>,
pub(crate) ledger_path: Option<String>,
pub(crate) baseline_delta_path: Option<String>,
pub(crate) zero_status_path: Option<String>,
pub(crate) coverage_json: Option<Result<String, String>>,
pub(crate) ledger_json: Option<Result<String, String>>,
pub(crate) baseline_delta_json: Option<Result<String, String>>,
pub(crate) zero_status_json: Option<Result<String, String>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct CoverageGripFrontierReport {
root: String,
generated_at: String,
status: String,
inputs: FrontierInputs,
coverage: CoverageAxis,
ripr: RiprAxis,
quadrants: CoverageQuadrants,
interpretation: String,
warnings: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct FrontierInputs {
coverage: Option<String>,
pr_evidence_ledger: Option<String>,
baseline_debt_delta: Option<String>,
ripr_zero_status: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct CoverageAxis {
status: String,
delta_percent: Option<String>,
source: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct RiprAxis {
source: Option<String>,
new_policy_eligible: usize,
baseline_resolved: usize,
baseline_still_present: usize,
acknowledged: usize,
suppressed: usize,
blocking_candidates: usize,
visible_unresolved: usize,
visible_unresolved_delta: Option<i64>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct CoverageQuadrants {
covered_with_ripr_gap: usize,
covered_without_ripr_gap: usize,
uncovered_with_ripr_gap: usize,
uncovered_without_ripr_gap: usize,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct ParsedSources {
coverage: Option<Value>,
ledger: Option<Value>,
baseline_delta: Option<Value>,
zero_status: Option<Value>,
warnings: Vec<String>,
}
pub(crate) fn build_coverage_grip_frontier_report(
input: CoverageGripFrontierInput,
) -> CoverageGripFrontierReport {
let parsed = parse_sources(&input);
let coverage = coverage_axis(input.coverage_path.as_deref(), parsed.coverage.as_ref());
let ripr = ripr_axis(
parsed.ledger.as_ref(),
parsed.baseline_delta.as_ref(),
parsed.zero_status.as_ref(),
parsed.coverage.as_ref(),
);
let quadrants = coverage_quadrants(parsed.coverage.as_ref());
let mut warnings = parsed.warnings;
if coverage.status == "not_available" {
warnings.push("coverage input not supplied; coverage axis is not available".to_string());
} else if coverage.status == "unsupported" {
warnings.push("coverage input has no supported coverage/grip frontier fields".to_string());
}
if ripr.source.is_none() {
warnings.push(
"RIPR movement input missing; provide PR evidence ledger, baseline delta, or RIPR Zero status"
.to_string(),
);
}
let interpretation = interpretation(&coverage, &ripr);
let status = if ripr.source.is_some() {
"advisory"
} else {
"incomplete"
}
.to_string();
CoverageGripFrontierReport {
root: input.root,
generated_at: input.generated_at,
status,
inputs: FrontierInputs {
coverage: input.coverage_path,
pr_evidence_ledger: input.ledger_path,
baseline_debt_delta: input.baseline_delta_path,
ripr_zero_status: input.zero_status_path,
},
coverage,
ripr,
quadrants,
interpretation,
warnings,
}
}
pub(crate) fn render_coverage_grip_frontier_json(
report: &CoverageGripFrontierReport,
) -> Result<String, String> {
serde_json::to_string_pretty(&json!({
"schema_version": SCHEMA_VERSION,
"tool": "ripr",
"kind": REPORT_KIND,
"status": report.status,
"root": report.root,
"generated_at": report.generated_at,
"inputs": {
"coverage": report.inputs.coverage,
"pr_evidence_ledger": report.inputs.pr_evidence_ledger,
"baseline_debt_delta": report.inputs.baseline_debt_delta,
"ripr_zero_status": report.inputs.ripr_zero_status,
},
"coverage": {
"status": report.coverage.status,
"delta_percent": report.coverage.delta_percent,
"source": report.coverage.source,
},
"ripr": {
"source": report.ripr.source,
"new_policy_eligible": report.ripr.new_policy_eligible,
"baseline_resolved": report.ripr.baseline_resolved,
"baseline_still_present": report.ripr.baseline_still_present,
"acknowledged": report.ripr.acknowledged,
"suppressed": report.ripr.suppressed,
"blocking_candidates": report.ripr.blocking_candidates,
"visible_unresolved": report.ripr.visible_unresolved,
"visible_unresolved_delta": report.ripr.visible_unresolved_delta,
},
"quadrants": {
"covered_with_ripr_gap": report.quadrants.covered_with_ripr_gap,
"covered_without_ripr_gap": report.quadrants.covered_without_ripr_gap,
"uncovered_with_ripr_gap": report.quadrants.uncovered_with_ripr_gap,
"uncovered_without_ripr_gap": report.quadrants.uncovered_without_ripr_gap,
},
"interpretation": report.interpretation,
"warnings": report.warnings,
"limits_note": LIMITS_NOTE,
}))
.map_err(|err| format!("render coverage/grip frontier JSON failed: {err}"))
}
pub(crate) fn render_coverage_grip_frontier_markdown(
report: &CoverageGripFrontierReport,
) -> String {
let mut out = String::new();
out.push_str("# RIPR Coverage / Grip Frontier\n\n");
out.push_str(&format!("Status: {}\n\n", report.status));
out.push_str("Coverage axis:\n");
out.push_str(&format!("- Status: {}\n", report.coverage.status));
out.push_str(&format!(
"- Delta percent: {}\n",
report
.coverage
.delta_percent
.as_deref()
.unwrap_or("not_available")
));
out.push_str(&format!(
"- Source: {}\n\n",
report.coverage.source.as_deref().unwrap_or("not_available")
));
out.push_str("RIPR axis:\n");
out.push_str(&format!(
"- Source: {}\n",
report.ripr.source.as_deref().unwrap_or("not_available")
));
out.push_str(&format!(
"- New policy-eligible gaps: {}\n",
report.ripr.new_policy_eligible
));
out.push_str(&format!(
"- Baseline gaps resolved: {}\n",
report.ripr.baseline_resolved
));
out.push_str(&format!(
"- Existing baseline gaps still present: {}\n",
report.ripr.baseline_still_present
));
out.push_str(&format!(
"- Acknowledged gaps: {}\n",
report.ripr.acknowledged
));
out.push_str(&format!("- Suppressed gaps: {}\n", report.ripr.suppressed));
out.push_str(&format!(
"- Blocking candidates: {}\n",
report.ripr.blocking_candidates
));
out.push_str(&format!(
"- Visible unresolved gaps: {}\n",
report.ripr.visible_unresolved
));
out.push_str(&format!(
"- Visible unresolved delta: {}\n\n",
report
.ripr
.visible_unresolved_delta
.map(|value| value.to_string())
.unwrap_or_else(|| "not_available".to_string())
));
out.push_str("Quadrants:\n");
out.push_str(&format!(
"- Covered with RIPR gap: {}\n",
report.quadrants.covered_with_ripr_gap
));
out.push_str(&format!(
"- Covered without RIPR gap: {}\n",
report.quadrants.covered_without_ripr_gap
));
out.push_str(&format!(
"- Uncovered with RIPR gap: {}\n",
report.quadrants.uncovered_with_ripr_gap
));
out.push_str(&format!(
"- Uncovered without RIPR gap: {}\n\n",
report.quadrants.uncovered_without_ripr_gap
));
out.push_str("Interpretation:\n");
out.push_str(&format!("- {}\n\n", report.interpretation));
if !report.warnings.is_empty() {
out.push_str("Warnings:\n");
for warning in &report.warnings {
out.push_str(&format!("- {warning}\n"));
}
out.push('\n');
}
out.push_str("Limits:\n");
out.push_str(LIMITS_NOTE);
out.push('\n');
out
}
pub(crate) use crate::output::path::display_path;
fn parse_sources(input: &CoverageGripFrontierInput) -> ParsedSources {
let mut parsed = ParsedSources::default();
parsed.coverage = parse_optional("coverage", &input.coverage_json, &mut parsed.warnings);
parsed.ledger = parse_optional(
"PR evidence ledger",
&input.ledger_json,
&mut parsed.warnings,
);
parsed.baseline_delta = parse_optional(
"baseline debt delta",
&input.baseline_delta_json,
&mut parsed.warnings,
);
parsed.zero_status = parse_optional(
"RIPR Zero status",
&input.zero_status_json,
&mut parsed.warnings,
);
parsed
}
fn parse_optional(
label: &str,
source: &Option<Result<String, String>>,
warnings: &mut Vec<String>,
) -> Option<Value> {
match source {
Some(Ok(text)) => match serde_json::from_str::<Value>(text) {
Ok(value) => Some(value),
Err(err) => {
warnings.push(format!("{label} input is invalid JSON: {err}"));
None
}
},
Some(Err(err)) => {
warnings.push(err.clone());
None
}
None => None,
}
}
fn coverage_axis(path: Option<&str>, coverage: Option<&Value>) -> CoverageAxis {
let Some(coverage) = coverage else {
return CoverageAxis {
status: "not_available".to_string(),
delta_percent: None,
source: None,
};
};
let delta_percent = string_or_number_path(coverage, &["coverage_delta_percent"])
.or_else(|| string_or_number_path(coverage, &["coverage", "delta_percent"]))
.or_else(|| string_or_number_path(coverage, &["summary", "coverage_delta_percent"]));
let has_quadrants = has_any_path(
coverage,
&[
&["quadrants", "covered_with_ripr_gap"],
&["covered_with_ripr_gap"],
&["quadrants", "covered_without_ripr_gap"],
&["covered_without_ripr_gap"],
&["quadrants", "uncovered_with_ripr_gap"],
&["uncovered_with_ripr_gap"],
&["quadrants", "uncovered_without_ripr_gap"],
&["uncovered_without_ripr_gap"],
],
);
let status = if delta_percent.is_some() || has_quadrants {
"available"
} else {
"unsupported"
};
CoverageAxis {
status: status.to_string(),
delta_percent,
source: path.map(ToOwned::to_owned),
}
}
fn ripr_axis(
ledger: Option<&Value>,
baseline_delta: Option<&Value>,
zero_status: Option<&Value>,
coverage: Option<&Value>,
) -> RiprAxis {
let source = if ledger.is_some_and(has_ledger_movement) {
Some("pr_evidence_ledger".to_string())
} else if zero_status.is_some_and(has_zero_status_movement) {
Some("ripr_zero_status".to_string())
} else if baseline_delta.is_some_and(has_baseline_delta_movement) {
Some("baseline_debt_delta".to_string())
} else {
None
};
let new_policy_eligible = usize_path_from_sources(&[
(ledger, &["movement", "new_policy_eligible"]),
(zero_status, &["ripr_zero", "new_policy_eligible"]),
(baseline_delta, &["delta", "new_policy_eligible"]),
])
.unwrap_or(0);
let baseline_resolved = usize_path_from_sources(&[
(ledger, &["movement", "baseline_resolved"]),
(zero_status, &["baseline", "resolved"]),
(baseline_delta, &["delta", "resolved"]),
])
.unwrap_or(0);
let baseline_still_present = usize_path_from_sources(&[
(ledger, &["movement", "baseline_still_present"]),
(zero_status, &["baseline", "still_present"]),
(baseline_delta, &["delta", "still_present"]),
])
.unwrap_or(0);
let acknowledged = usize_path_from_sources(&[
(ledger, &["movement", "acknowledged"]),
(baseline_delta, &["delta", "acknowledged"]),
])
.unwrap_or(0);
let suppressed = usize_path_from_sources(&[
(ledger, &["movement", "suppressed"]),
(baseline_delta, &["delta", "suppressed"]),
])
.unwrap_or(0);
let blocking_candidates = usize_path_from_sources(&[
(ledger, &["movement", "blocking_candidates"]),
(zero_status, &["ripr_zero", "blocking_candidates"]),
])
.unwrap_or(0);
let visible_unresolved = usize_path_from_sources(&[
(ledger, &["movement", "visible_unresolved"]),
(zero_status, &["ripr_zero", "visible_unresolved"]),
])
.unwrap_or(baseline_still_present + new_policy_eligible + acknowledged);
let visible_unresolved_delta = coverage
.and_then(|value| i64_path(value, &["ripr_visible_unresolved_delta"]))
.or_else(|| {
coverage.and_then(|value| i64_path(value, &["ripr", "visible_unresolved_delta"]))
})
.or_else(|| {
ledger.and_then(|value| {
i64_path(
value,
&["coverage_grip_frontier", "ripr_visible_unresolved_delta"],
)
})
});
RiprAxis {
source,
new_policy_eligible,
baseline_resolved,
baseline_still_present,
acknowledged,
suppressed,
blocking_candidates,
visible_unresolved,
visible_unresolved_delta,
}
}
fn coverage_quadrants(coverage: Option<&Value>) -> CoverageQuadrants {
let Some(coverage) = coverage else {
return CoverageQuadrants::default();
};
CoverageQuadrants {
covered_with_ripr_gap: usize_path_any(
coverage,
&[
&["quadrants", "covered_with_ripr_gap"],
&["covered_with_ripr_gap"],
],
),
covered_without_ripr_gap: usize_path_any(
coverage,
&[
&["quadrants", "covered_without_ripr_gap"],
&["covered_without_ripr_gap"],
],
),
uncovered_with_ripr_gap: usize_path_any(
coverage,
&[
&["quadrants", "uncovered_with_ripr_gap"],
&["uncovered_with_ripr_gap"],
],
),
uncovered_without_ripr_gap: usize_path_any(
coverage,
&[
&["quadrants", "uncovered_without_ripr_gap"],
&["uncovered_without_ripr_gap"],
],
),
}
}
fn interpretation(coverage: &CoverageAxis, ripr: &RiprAxis) -> String {
if coverage.status == "not_available" {
return "coverage input not supplied; behavioral grip movement remains visible on the RIPR axis"
.to_string();
}
if coverage.status == "unsupported" {
return "coverage input is present but unsupported; keep coverage and RIPR movement separate"
.to_string();
}
if coverage_delta_is_zero(coverage.delta_percent.as_deref()) {
if ripr.baseline_resolved > 0 || ripr.visible_unresolved_delta.unwrap_or(0) < 0 {
return "behavioral grip improved without line-coverage movement".to_string();
}
return "coverage was flat; inspect RIPR movement separately".to_string();
}
if ripr.new_policy_eligible > 0 {
return "coverage changed while new policy-eligible RIPR gaps remain visible".to_string();
}
"coverage and RIPR movement are reported separately; coverage is execution evidence, not adequacy"
.to_string()
}
fn path_value<'a>(value: &'a Value, path: &[&str]) -> Option<&'a Value> {
let mut current = value;
for key in path {
current = current.get(*key)?;
}
Some(current)
}
fn string_or_number_path(value: &Value, path: &[&str]) -> Option<String> {
let value = path_value(value, path)?;
if let Some(text) = value.as_str() {
return Some(text.to_string());
}
if let Some(number) = value.as_i64() {
return Some(number.to_string());
}
value.as_f64().map(|number| number.to_string())
}
fn i64_path(value: &Value, path: &[&str]) -> Option<i64> {
path_value(value, path).and_then(value_as_i64)
}
fn usize_path(value: &Value, path: &[&str]) -> Option<usize> {
path_value(value, path).and_then(value_as_usize)
}
fn usize_path_from_sources(sources: &[(Option<&Value>, &[&str])]) -> Option<usize> {
sources
.iter()
.find_map(|(value, path)| value.and_then(|value| usize_path(value, path)))
}
fn usize_path_any(value: &Value, paths: &[&[&str]]) -> usize {
paths
.iter()
.find_map(|path| usize_path(value, path))
.unwrap_or(0)
}
fn value_as_i64(value: &Value) -> Option<i64> {
if let Some(number) = value.as_i64() {
return Some(number);
}
if let Some(number) = value.as_u64() {
return i64::try_from(number).ok();
}
if let Some(text) = value.as_str() {
return text
.trim()
.parse::<i64>()
.ok()
.or_else(|| i64_from_f64_text(text));
}
value.as_f64().and_then(i64_from_f64)
}
fn value_as_usize(value: &Value) -> Option<usize> {
if let Some(number) = value.as_u64() {
return usize::try_from(number).ok();
}
if let Some(number) = value.as_i64() {
return u64::try_from(number)
.ok()
.and_then(|number| usize::try_from(number).ok());
}
if let Some(text) = value.as_str() {
return text
.trim()
.parse::<u64>()
.ok()
.and_then(|number| usize::try_from(number).ok())
.or_else(|| usize_from_f64_text(text));
}
value.as_f64().and_then(usize_from_f64)
}
fn i64_from_f64_text(text: &str) -> Option<i64> {
text.trim().parse::<f64>().ok().and_then(i64_from_f64)
}
fn usize_from_f64_text(text: &str) -> Option<usize> {
text.trim().parse::<f64>().ok().and_then(usize_from_f64)
}
fn i64_from_f64(number: f64) -> Option<i64> {
if number.is_finite() && number.fract() == 0.0 {
return format!("{number:.0}").parse::<i64>().ok();
}
None
}
fn usize_from_f64(number: f64) -> Option<usize> {
if number.is_finite() && number.fract() == 0.0 && number >= 0.0 {
return format!("{number:.0}").parse::<usize>().ok();
}
None
}
fn coverage_delta_is_zero(delta_percent: Option<&str>) -> bool {
delta_percent
.and_then(|value| value.trim().parse::<f64>().ok())
.is_some_and(|value| value.is_finite() && value == 0.0)
}
fn has_any_path(value: &Value, paths: &[&[&str]]) -> bool {
paths.iter().any(|path| path_value(value, path).is_some())
}
fn has_ledger_movement(value: &Value) -> bool {
has_any_path(
value,
&[
&["movement", "new_policy_eligible"],
&["movement", "baseline_resolved"],
&["movement", "baseline_still_present"],
&["movement", "acknowledged"],
&["movement", "suppressed"],
&["movement", "blocking_candidates"],
&["movement", "visible_unresolved"],
],
)
}
fn has_zero_status_movement(value: &Value) -> bool {
has_any_path(
value,
&[
&["ripr_zero", "new_policy_eligible"],
&["ripr_zero", "blocking_candidates"],
&["ripr_zero", "visible_unresolved"],
&["baseline", "resolved"],
&["baseline", "still_present"],
],
)
}
fn has_baseline_delta_movement(value: &Value) -> bool {
has_any_path(
value,
&[
&["delta", "new_policy_eligible"],
&["delta", "resolved"],
&["delta", "still_present"],
&["delta", "acknowledged"],
&["delta", "suppressed"],
],
)
}
#[cfg(test)]
mod tests {
use super::*;
fn report_with(
coverage_json: Option<Result<String, String>>,
ledger_json: Option<Result<String, String>>,
baseline_delta_json: Option<Result<String, String>>,
zero_status_json: Option<Result<String, String>>,
) -> CoverageGripFrontierReport {
build_coverage_grip_frontier_report(CoverageGripFrontierInput {
root: ".".to_string(),
generated_at: "unix_ms:1000".to_string(),
coverage_path: coverage_json
.as_ref()
.map(|_| "target/ripr/reports/coverage-summary.json".to_string()),
ledger_path: ledger_json
.as_ref()
.map(|_| "target/ripr/reports/pr-evidence-ledger.json".to_string()),
baseline_delta_path: baseline_delta_json
.as_ref()
.map(|_| "target/ripr/reports/baseline-debt-delta.json".to_string()),
zero_status_path: zero_status_json
.as_ref()
.map(|_| "target/ripr/reports/ripr-zero-status.json".to_string()),
coverage_json,
ledger_json,
baseline_delta_json,
zero_status_json,
})
}
#[test]
fn coverage_grip_frontier_reports_flat_coverage_with_improved_grip() -> Result<(), String> {
let coverage = r#"{
"coverage_delta_percent": 0.0,
"ripr_visible_unresolved_delta": -3,
"quadrants": {
"covered_with_ripr_gap": 4,
"covered_without_ripr_gap": 20,
"uncovered_with_ripr_gap": 2,
"uncovered_without_ripr_gap": 8
}
}"#;
let ledger = r#"{
"movement": {
"new_policy_eligible": 1,
"baseline_resolved": 3,
"baseline_still_present": 2,
"acknowledged": 1,
"suppressed": 0,
"blocking_candidates": 0,
"visible_unresolved": 4
}
}"#;
let report = report_with(
Some(Ok(coverage.to_string())),
Some(Ok(ledger.to_string())),
None,
None,
);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains(r#""kind": "coverage_grip_frontier""#));
assert!(rendered.contains(r#""delta_percent": "0""#));
assert!(rendered.contains(r#""visible_unresolved_delta": -3"#));
assert!(rendered.contains("behavioral grip improved without line-coverage movement"));
let markdown = render_coverage_grip_frontier_markdown(&report);
assert!(markdown.contains("Coverage axis:"));
assert!(markdown.contains("Baseline gaps resolved: 3"));
assert!(markdown.contains("Covered with RIPR gap: 4"));
Ok(())
}
#[test]
fn coverage_grip_frontier_keeps_missing_coverage_advisory() -> Result<(), String> {
let delta = r#"{"delta":{"new_policy_eligible":2,"resolved":0,"still_present":5,"acknowledged":1,"suppressed":1}}"#;
let report = report_with(None, None, Some(Ok(delta.to_string())), None);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains(r#""status": "not_available""#));
assert!(rendered.contains(r#""source": "baseline_debt_delta""#));
assert!(rendered.contains("coverage input not supplied"));
assert!(
rendered.contains(
"Coverage is execution evidence; RIPR is static behavioral grip evidence"
)
);
Ok(())
}
#[test]
fn coverage_grip_frontier_reports_incomplete_without_ripr_movement() -> Result<(), String> {
let report = report_with(
Some(Ok(r#"{"coverage_delta_percent": 2.0}"#.to_string())),
None,
None,
None,
);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains(r#""status": "incomplete""#));
assert!(rendered.contains("RIPR movement input missing"));
assert!(rendered.contains("coverage is execution evidence, not adequacy"));
Ok(())
}
#[test]
fn coverage_grip_frontier_warns_on_invalid_inputs() -> Result<(), String> {
let zero = r#"{"ripr_zero":{"visible_unresolved":1,"new_policy_eligible":0,"blocking_candidates":0},"baseline":{"resolved":1,"still_present":1}}"#;
let report = report_with(
Some(Ok("{}".to_string())),
Some(Err("read ledger failed".to_string())),
None,
Some(Ok(zero.to_string())),
);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains("read ledger failed"));
assert!(rendered.contains("coverage input has no supported coverage/grip frontier fields"));
assert!(rendered.contains(r#""source": "ripr_zero_status""#));
Ok(())
}
#[test]
fn coverage_grip_frontier_parses_string_encoded_numbers() -> Result<(), String> {
let coverage = r#"{
"coverage_delta_percent": "0.00",
"ripr_visible_unresolved_delta": "-2",
"quadrants": {
"covered_with_ripr_gap": "4",
"covered_without_ripr_gap": "20",
"uncovered_with_ripr_gap": "2.0",
"uncovered_without_ripr_gap": "8"
}
}"#;
let ledger = r#"{
"movement": {
"new_policy_eligible": "1",
"baseline_resolved": "2",
"baseline_still_present": "3",
"acknowledged": "0",
"suppressed": "0",
"blocking_candidates": "0",
"visible_unresolved": "4"
}
}"#;
let report = report_with(
Some(Ok(coverage.to_string())),
Some(Ok(ledger.to_string())),
None,
None,
);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains(r#""baseline_resolved": 2"#));
assert!(rendered.contains(r#""uncovered_with_ripr_gap": 2"#));
assert!(rendered.contains(r#""visible_unresolved_delta": -2"#));
assert!(rendered.contains("behavioral grip improved without line-coverage movement"));
Ok(())
}
#[test]
fn coverage_grip_frontier_falls_back_when_preferred_source_lacks_metric() -> Result<(), String>
{
let ledger = r#"{"movement":{"new_policy_eligible":0}}"#;
let delta =
r#"{"delta":{"resolved":7,"still_present":11,"acknowledged":2,"suppressed":1}}"#;
let report = report_with(
None,
Some(Ok(ledger.to_string())),
Some(Ok(delta.to_string())),
None,
);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains(r#""source": "pr_evidence_ledger""#));
assert!(rendered.contains(r#""new_policy_eligible": 0"#));
assert!(rendered.contains(r#""baseline_resolved": 7"#));
assert!(rendered.contains(r#""baseline_still_present": 11"#));
assert!(rendered.contains(r#""acknowledged": 2"#));
assert!(rendered.contains(r#""suppressed": 1"#));
Ok(())
}
#[test]
fn coverage_grip_frontier_skips_empty_preferred_sources() -> Result<(), String> {
let zero = r#"{"ripr_zero":{"visible_unresolved":5,"new_policy_eligible":1,"blocking_candidates":0},"baseline":{"resolved":2,"still_present":4}}"#;
let report = report_with(
None,
Some(Ok("{}".to_string())),
None,
Some(Ok(zero.to_string())),
);
let rendered = render_coverage_grip_frontier_json(&report)?;
assert!(rendered.contains(r#""source": "ripr_zero_status""#));
assert!(rendered.contains(r#""visible_unresolved": 5"#));
assert!(rendered.contains(r#""baseline_resolved": 2"#));
Ok(())
}
}