use super::*;
fn rec(line: &str) -> Record {
serde_json::from_slice(line.as_bytes()).expect("valid fixture record")
}
fn extract(records: &[Record]) -> Vec<TaggedMutation> {
let line_nos: Vec<usize> = (1..=records.len()).collect();
extract_mutations("0a1b2c3d-sess", records, &line_nos)
}
fn fixture() -> Vec<Record> {
vec![
rec(
r#"{"type":"user","uuid":"u0","timestamp":"2026-06-07T05:00:00.000Z","message":{"role":"user","content":"set up the docs"}}"#,
),
rec(
r#"{"type":"assistant","uuid":"a0","timestamp":"2026-06-07T05:00:01.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"w1","name":"Write","input":{"file_path":"/tmp/a.md","content":"x"}}]}}"#,
),
rec(
r#"{"type":"user","uuid":"c0","toolUseResult":{"type":"create","filePath":"/tmp/a.md"},"message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"w1","content":"ok"}]}}"#,
),
rec(
r#"{"type":"assistant","uuid":"a1","timestamp":"2026-06-07T05:00:02.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"w2","name":"Write","input":{"file_path":"/tmp/b.md","content":"y"}}]}}"#,
),
rec(
r#"{"type":"user","uuid":"c1","toolUseResult":{"type":"create","filePath":"/tmp/b.md"},"message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"w2","content":"ok"}]}}"#,
),
rec(
r#"{"type":"assistant","uuid":"a2","timestamp":"2026-06-07T05:00:03.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"e1","name":"Edit","input":{"file_path":"/p/spec/gaps.md","old_string":"a","new_string":"b"}}]}}"#,
),
rec(
r#"{"type":"user","uuid":"c2","toolUseResult":{"type":"update","filePath":"/p/spec/gaps.md"},"message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"e1","content":"ok"}]}}"#,
),
rec(
r#"{"type":"user","uuid":"meta","isMeta":true,"timestamp":"2026-06-07T05:01:00.000Z","message":{"role":"user","content":"Continue from where you left off."}}"#,
),
rec(
r#"{"type":"user","uuid":"u1","timestamp":"2026-06-07T06:00:00.000Z","message":{"role":"user","content":"clean up and refactor"}}"#,
),
rec(
r#"{"type":"assistant","uuid":"a3","timestamp":"2026-06-07T06:00:01.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"b1","name":"Bash","input":{"command":"rm /tmp/a.md"}}]}}"#,
),
rec(
r#"{"type":"assistant","uuid":"a4","timestamp":"2026-06-07T06:00:02.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"m1","name":"MultiEdit","input":{"file_path":"/p/src/lib.rs","edits":[]}}]}}"#,
),
rec(
r#"{"type":"user","uuid":"c3","toolUseResult":{"type":"update","filePath":"/p/src/lib.rs"},"message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"m1","content":"ok"}]}}"#,
),
]
}
mod buckets;
mod filters;
mod mutations;
mod render;
fn outcome(detail: FilesDetail, muts: Vec<TaggedMutation>) -> Outcome {
let mut subs = std::collections::BTreeSet::new();
let mut tops = std::collections::BTreeSet::new();
for m in &muts {
if m.is_subagent {
subs.insert(m.session_id.clone());
} else {
tops.insert(m.session_id.clone());
}
}
let (scope_top, scope_sub) = (tops.len(), subs.len());
Outcome {
detail,
mutations: muts,
boundaries: Vec::new(),
skipped_lines: 0,
turn_range: None,
time_window_bounded: false,
scope_top,
scope_sub,
}
}