Skip to main content

speccoder_runtime/pipeline/
samples.rs

1//! Best-of-n: choosing among independent samples by evidence.
2
3use crate::worktree;
4
5/// One sample's evidence: verdict, surviving mutants, diff size, tokens.
6pub(crate) fn sample_summary(
7    report: &speccoder_core::RunReport,
8    status: &str,
9    patch: &str,
10) -> serde_json::Value {
11    use speccoder_core::verify::Verdict;
12    let rank = match (status, &report.verdict) {
13        ("completed", Some(Verdict::Verified)) => 2,
14        ("completed", Some(Verdict::RegressionOnly)) => 1,
15        _ => 0,
16    };
17    let changed = patch
18        .lines()
19        .filter(|l| {
20            (l.starts_with('+') && !l.starts_with("+++"))
21                || (l.starts_with('-') && !l.starts_with("---"))
22        })
23        .count();
24    let tokens = report.usage.as_ref().map_or(0, |u| {
25        u["input_tokens"].as_u64().unwrap_or(0) + u["output_tokens"].as_u64().unwrap_or(0)
26    });
27    serde_json::json!({
28        "run_id": report.run_id,
29        "attempt_status": status,
30        "verdict": report.verdict,
31        "rank": rank,
32        "survivors": report.mutation.as_ref().and_then(|m| m["missed"].as_u64()),
33        "changed_lines": changed,
34        "tokens": tokens,
35    })
36}
37
38/// The best sample: passing first (verified over regression-only), then
39/// fewer surviving mutants, a smaller diff, fewer tokens; ties keep the earlier.
40pub(crate) fn best_sample(
41    samples: &[(
42        speccoder_core::RunReport,
43        String,
44        worktree::Checkpoint,
45        serde_json::Value,
46    )],
47) -> usize {
48    let key = |s: &serde_json::Value| {
49        (
50            std::cmp::Reverse(s["rank"].as_u64().unwrap_or(0)),
51            s["survivors"].as_u64().unwrap_or(0),
52            s["changed_lines"].as_u64().unwrap_or(0),
53            s["tokens"].as_u64().unwrap_or(0),
54        )
55    };
56    (0..samples.len())
57        .min_by_key(|&i| (key(&samples[i].3), i))
58        .unwrap_or(0)
59}