use crate::run::{E2eStatus, ReviewRound, RunState};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FinalHead<'a> {
Unreported,
Unknown,
At(&'a str),
}
fn short(sha: &str) -> &str {
sha.get(..7).unwrap_or(sha)
}
fn all_ok(outcomes: &[crate::run::CommandOutcome]) -> bool {
!outcomes.is_empty() && outcomes.iter().all(|o| o.ok())
}
pub fn review_rounds(state: &RunState) -> String {
let mut out = String::new();
for r in &state.reviews {
let Some(fix) = &r.fix else { continue };
if out.is_empty() {
out.push_str("## Review rounds\n\n");
}
out.push_str(&format!("### Round {}\n\n", r.round));
out.push_str(if fix.committed {
"The fix is committed on the branch (including work committed by magi).\n\n"
} else {
"This round produced no commit.\n\n"
});
for id in &fix.addressed {
out.push_str(&format!("- Addressed: `{id}`\n"));
}
for rejection in &fix.rejected {
out.push_str(&format!("- Declined in this round: `{}`\n", rejection.id));
}
out.push('\n');
}
out
}
pub fn summary_evidence(state: &RunState, head: FinalHead<'_>) -> String {
let open = state.last_round_findings();
let declined: Vec<_> = state
.reviews
.iter()
.filter_map(|r| r.fix.as_ref())
.flat_map(|f| &f.rejected)
.map(|rejection| {
let finding = state
.reviews
.iter()
.flat_map(|r| &r.reviews)
.flat_map(|r| &r.findings)
.find(|f| f.id == rejection.id);
serde_json::json!({"id": rejection.id, "reason": rejection.why,
"finding": finding})
})
.collect();
let verification = serde_json::json!({
"gate": state.gate.iter().map(|o| (&o.command, o.code, o.resource_blocked)).collect::<Vec<_>>(),
"tests": state.reviews.iter().map(|r| (r.round, &r.verified_head,
r.e2e.iter().map(|o| (&o.command, o.code, o.resource_blocked)).collect::<Vec<_>>())).collect::<Vec<_>>()
});
format!(
"{}{}\nOpen findings (local source material):\n{}\nDeclined findings (local source material):\n{}\nVerification commands and outcomes:\n{verification}",
review_rounds(state),
final_verification(state, head),
serde_json::to_string(&open).unwrap_or_default(),
serde_json::to_string(&declined).unwrap_or_default()
)
}
pub fn summary_fingerprint(state: &RunState, evidence: &str) -> String {
crate::github_text::fingerprint(
&format!(
"public-description-v3:{}",
state.config.graph.github_language
),
evidence,
)
}
pub fn required_finding_ids(state: &RunState) -> Vec<String> {
let mut ids: Vec<_> = state
.last_round_findings()
.iter()
.map(|f| f.id.clone())
.collect();
ids.extend(
state
.reviews
.iter()
.filter_map(|r| r.fix.as_ref())
.flat_map(|f| f.rejected.iter().map(|r| r.id.clone())),
);
ids.sort();
ids.dedup();
ids
}
pub fn covers_required_findings(state: &RunState, prose: &str) -> bool {
required_finding_ids(state).iter().all(|id| {
prose
.split(|c: char| !(c.is_ascii_alphanumeric() || c == '-'))
.any(|word| word == id)
})
}
fn last_checked(state: &RunState) -> Option<&ReviewRound> {
state
.reviews
.iter()
.rev()
.find(|r| r.e2e_status() != E2eStatus::NotConfigured)
}
pub fn final_verification(state: &RunState, head: FinalHead<'_>) -> String {
if head == FinalHead::Unreported {
return String::new();
}
let at = match head {
FinalHead::At(h) => Some(h),
_ => None,
};
let mut lines = vec![match at {
Some(h) => format!("- Final commit: `{}`", short(h)),
None => "- Final commit: unknown, so no check below is claimed for it".to_owned(),
}];
let ran_on = |recorded: Option<&str>| -> String {
match (at, recorded) {
(Some(h), Some(r)) if h == r => "on the final commit".to_owned(),
(_, Some(r)) => format!(
"on an earlier commit (`{}`); later changes were not re-verified by it",
short(r)
),
(_, None) => "on a commit that was not recorded".to_owned(),
}
};
match last_checked(state) {
None => lines.push("- Tests (`verify.e2e`): not configured or never run".to_owned()),
Some(r) => lines.push(match r.e2e_status() {
E2eStatus::Passed => format!(
"- Tests (`verify.e2e`): passed {} (round {})",
ran_on(r.verified_head.as_deref()),
r.round
),
E2eStatus::Failed => format!(
"- Tests (`verify.e2e`): FAILED {} (round {})",
ran_on(r.verified_head.as_deref()),
r.round
),
E2eStatus::Deferred => format!(
"- Tests (`verify.e2e`): deferred in round {}, not run",
r.round
),
E2eStatus::ResourceBlocked => format!(
"- Tests (`verify.e2e`): not run in round {} (blocked on a shared resource)",
r.round
),
E2eStatus::NotConfigured => unreachable!(),
}),
}
lines.push(if !state.gate_ran {
"- Gate (`verify.gate`): not run".to_owned()
} else if state.gate.is_empty() {
"- Gate (`verify.gate`): no commands configured".to_owned()
} else if !all_ok(&state.gate) {
format!(
"- Gate (`verify.gate`): FAILED {}",
ran_on(state.gate_head.as_deref())
)
} else {
format!(
"- Gate (`verify.gate`): passed {}",
ran_on(state.gate_head.as_deref())
)
});
if !state.gate_fixes.is_empty() {
lines.push(format!(
"- Gate-fix rounds spent: {}",
state.gate_fixes.len()
));
}
if !state.rebase_fixes.is_empty() {
lines.push(format!(
"- Rebase conflict rounds spent: {}",
state.rebase_fixes.len()
));
}
format!("## Final verification\n\n{}\n\n", lines.join("\n"))
}