use super::*;
use std::io::Write as _;
use std::sync::atomic::{AtomicU64, Ordering};
mod lineage;
#[test]
fn truncate_short_excerpt_unchanged() {
assert_eq!(truncate_excerpt("hello"), "hello");
}
#[test]
fn truncate_long_excerpt_marks_dropped_count() {
let s = "x".repeat(EXCERPT_MAX + 5);
let out = truncate_excerpt(&s);
assert!(out.ends_with("… (+5 chars)"), "got: {out}");
assert!(out.starts_with(&"x".repeat(EXCERPT_MAX)));
}
#[test]
fn truncate_counts_chars_not_bytes() {
let s = "🛠".repeat(EXCERPT_MAX + 2);
let out = truncate_excerpt(&s);
assert!(out.ends_with("… (+2 chars)"), "got: {out}");
}
#[test]
fn format_timestamp_uses_system_local_canonical_marker() {
let raw = "2026-06-07T05:48:22.880Z";
let out = format_timestamp(Some(raw));
let ts: jiff::Timestamp = raw.parse().expect("parseable instant");
let local = ts
.to_zoned(crate::timez::local_tz())
.strftime("%Y-%m-%d %H:%M:%S")
.to_string();
assert!(out.contains(&local), "expected local {local:?} in {out:?}");
assert!(out.contains("(UTC"), "marker missing: {out}");
assert!(!out.contains(raw), "the raw-UTC copy must be gone: {out}");
}
#[test]
fn format_timestamp_missing_is_em_dash() {
assert_eq!(format_timestamp(None), "—");
}
#[test]
fn format_timestamp_unparseable_surfaces_raw() {
let out = format_timestamp(Some("not-a-time"));
assert!(out.contains("not-a-time"));
assert!(out.contains("unparsed"));
}
#[test]
fn local_iso_matches_system_tz_offset() {
let raw = "2026-06-07T05:48:22.880Z";
let out = local_iso(raw).expect("local iso");
let ts: jiff::Timestamp = raw.parse().expect("parseable instant");
let expected = ts
.to_zoned(crate::timez::local_tz())
.strftime("%Y-%m-%dT%H:%M:%S%:z")
.to_string();
assert_eq!(out, expected);
}
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn tmp_session(name_stem: &str, lines: &[&str]) -> PathBuf {
let p = std::env::temp_dir().join(format!(
"csift-sess-{}-{}-{name_stem}.jsonl",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
));
let mut f = std::fs::File::create(&p).unwrap();
for l in lines {
writeln!(f, "{l}").unwrap();
}
p
}
#[test]
fn capture_identity_if_empty_only_fills_blanks() {
let rec: Record = serde_json::from_str(
r#"{"type":"user","cwd":"/c","version":"9.9","gitBranch":"b","sessionId":"sid","message":{"role":"user","content":"x"}}"#,
)
.unwrap();
let mut cwd = None;
let mut version = Some("keep".to_string()); let mut branch = None;
let mut sid = None;
capture_identity_if_empty(&rec, &mut cwd, &mut version, &mut branch, &mut sid);
assert_eq!(cwd.as_deref(), Some("/c"));
assert_eq!(version.as_deref(), Some("keep"), "pre-set value preserved");
assert_eq!(branch.as_deref(), Some("b"));
assert_eq!(sid.as_deref(), Some("sid"));
}
#[test]
fn summarize_head_first_user_captures_identity() {
let p = tmp_session(
"head",
&[
r#"{"type":"user","cwd":"/Users/testuser/Projects/foo","version":"2.1.0","gitBranch":"main","sessionId":"sid-data","message":{"role":"user","content":"first q"}}"#,
r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"mid agent"}]}}"#,
r#"{"type":"user","message":{"role":"user","content":"last q"}}"#,
r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"last agent"}]}}"#,
],
);
let s = summarize_session(&p, false).unwrap();
std::fs::remove_file(&p).ok();
assert_eq!(s.cwd.as_deref(), Some("/Users/testuser/Projects/foo"));
assert_eq!(s.version.as_deref(), Some("2.1.0"));
assert_eq!(s.git_branch.as_deref(), Some("main"));
assert_eq!(s.first_user.as_ref().unwrap().excerpt, "first q");
assert_eq!(s.last_user.as_ref().unwrap().excerpt, "last q");
assert_eq!(s.last_agent.as_ref().unwrap().excerpt, "last agent");
}
#[test]
fn summarize_backfills_identity_from_tail_when_head_user_lacks_it() {
let p = tmp_session(
"tailfill",
&[
r#"{"type":"user","message":{"role":"user","content":"first q, no identity"}}"#,
r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"a"}]}}"#,
r#"{"type":"user","cwd":"/tail/cwd","version":"3.0","gitBranch":"dev","sessionId":"sid-tail","message":{"role":"user","content":"last q, has identity"}}"#,
],
);
let s = summarize_session(&p, false).unwrap();
std::fs::remove_file(&p).ok();
assert_eq!(
s.first_user.as_ref().unwrap().excerpt,
"first q, no identity"
);
assert_eq!(
s.last_user.as_ref().unwrap().excerpt,
"last q, has identity"
);
assert_eq!(s.cwd.as_deref(), Some("/tail/cwd"));
assert_eq!(s.version.as_deref(), Some("3.0"));
assert_eq!(s.git_branch.as_deref(), Some("dev"));
}
#[test]
fn summarize_session_id_from_data_when_filename_has_no_stem() {
let p = tmp_session(
"dataid",
&[r#"{"type":"user","sessionId":"sid-data-xyz","message":{"role":"user","content":"hi"}}"#],
);
let s = summarize_session(&p, false).unwrap();
std::fs::remove_file(&p).ok();
assert!(!s.session_id.is_empty());
}
#[test]
fn summarize_head_skips_non_genuine_records_before_first_user() {
let p = tmp_session(
"headnoise",
&[
r#"{"type":"last-prompt","leafUuid":"x"}"#,
r#"{"type":"user","isMeta":true,"message":{"role":"user","content":"Continue from where you left off."}}"#,
r#"{"type":"user","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"x","content":"carrier"}]}}"#,
r#"{"type":"user","message":{"role":"user","content":"the genuine first question"}}"#,
r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"a"}]}}"#,
],
);
let s = summarize_session(&p, false).unwrap();
std::fs::remove_file(&p).ok();
assert_eq!(
s.first_user.as_ref().unwrap().excerpt,
"the genuine first question"
);
}
#[test]
fn top_level_summary_is_not_subagent_and_is_its_own_parent() {
let p = tmp_session(
"toplevel",
&[r#"{"type":"user","message":{"role":"user","content":"hi"}}"#],
);
let s = summarize_session(&p, false).unwrap();
let stem = p.file_stem().unwrap().to_str().unwrap().to_string();
std::fs::remove_file(&p).ok();
assert!(!s.is_subagent);
assert_eq!(s.parent_session_id, stem);
assert_eq!(s.parent_session_id, s.session_id);
}
#[test]
fn subagent_summary_carries_is_subagent_and_refeedable_parent() {
let dir = std::env::temp_dir().join(format!(
"csift-subdir-{}-{}/11111111-2222-3333-4444-555555555555/subagents/workflows/wf_z",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&dir).unwrap();
let p = dir.join("agent-deadbeefcafe1234.jsonl");
{
let mut f = std::fs::File::create(&p).unwrap();
writeln!(
f,
r#"{{"type":"user","message":{{"role":"user","content":"sub work"}}}}"#
)
.unwrap();
}
let s = summarize_session(&p, false).unwrap();
std::fs::remove_file(&p).ok();
assert_eq!(
s.session_id, "deadbeefcafe1234",
"bare hex (agent- stripped)"
);
assert!(s.is_subagent, "a subagents/ path is a subagent transcript");
assert_eq!(
s.parent_session_id, "11111111-2222-3333-4444-555555555555",
"parent is the re-feedable uuid dir before subagents/"
);
}
#[test]
fn summarize_session_id_is_filename_stem() {
let p = tmp_session(
"stemid",
&[r#"{"type":"user","sessionId":"DATA-ID","message":{"role":"user","content":"hi"}}"#],
);
let s = summarize_session(&p, false).unwrap();
let stem = p.file_stem().unwrap().to_str().unwrap().to_string();
std::fs::remove_file(&p).ok();
assert_eq!(s.session_id, stem);
assert_ne!(s.session_id, "DATA-ID");
}
#[test]
fn clone_probe_arms_and_origin_join_edges() {
static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let root = std::env::temp_dir().join(format!("csift-clone-{}-{n}", std::process::id()));
std::fs::create_dir_all(&root).unwrap();
let bx = "0f1e2d3c-4b5a-4697-8807-16f5e4d3c2b1";
let clone = root.join("c1.jsonl");
std::fs::write(
&clone,
format!(
"not json but mentions \"timestamp\" anyway\n{{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"{bx}\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}}\n"
),
)
.unwrap();
assert_eq!(
clone_head_boundary(&clone).unwrap().as_deref(),
Some(bx),
"garbage before the boundary is walked past"
);
let empty = root.join("c0.jsonl");
std::fs::write(&empty, "").unwrap();
assert_eq!(clone_head_boundary(&empty).unwrap(), None);
let blank = root.join("c3.jsonl");
std::fs::write(
&blank,
"{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}\n",
)
.unwrap();
assert_eq!(clone_head_boundary(&blank).unwrap(), None);
let anon = root.join("c2.jsonl");
std::fs::write(
&anon,
"{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}\n",
)
.unwrap();
assert_eq!(clone_head_boundary(&anon).unwrap(), None);
let origin = root.join("o1.jsonl");
std::fs::write(
&origin,
format!(
"{{\"type\":\"user\",\"uuid\":\"u1\",\"timestamp\":\"2026-06-07T05:00:00.000Z\",\"message\":{{\"role\":\"user\",\"content\":\"discussing {bx} in prose\"}}}}\n{{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"{bx}\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}}\n"
),
)
.unwrap();
assert_eq!(
clone_origin(&clone, bx).as_deref(),
Some("o1"),
"prose quote is skipped, the record wins"
);
std::fs::remove_file(&origin).unwrap();
let bystander = root.join("b1.jsonl");
std::fs::write(
&bystander,
format!(
"{{\"type\":\"user\",\"uuid\":\"u1\",\"timestamp\":\"2026-06-07T05:00:00.000Z\",\"message\":{{\"role\":\"user\",\"content\":\"only prose about {bx}\"}}}}\n"
),
)
.unwrap();
assert_eq!(clone_origin(&clone, bx), None);
let decoy = root.join("d1.jsonl");
std::fs::write(
&decoy,
format!(
"{{\"type\":\"user\",\"uuid\":\"u1\",\"timestamp\":\"2026-06-07T05:00:00.000Z\",\"message\":{{\"role\":\"user\",\"content\":\"prose about {bx}\"}}}}\n{{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"99999999-aaaa-4bbb-8ccc-dddddddddddd\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}}\n"
),
)
.unwrap();
assert_eq!(
clone_origin(&clone, bx),
None,
"an unrelated boundary near a prose quote is not the origin"
);
std::fs::remove_file(&decoy).unwrap();
std::fs::remove_dir_all(&root).unwrap();
}
#[test]
fn clone_origin_decoys_stay_outside_the_join() {
let root = std::env::temp_dir().join(format!("csift-clone-decoy-{}", std::process::id()));
std::fs::create_dir_all(&root).unwrap();
let bx = "3a4b5c6d-7e8f-4a9b-8c0d-1e2f3a4b5c6d";
let clone = root.join("c1.jsonl");
std::fs::write(
&clone,
format!(
"{{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"{bx}\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}}\n"
),
)
.unwrap();
let txt = root.join("o1.txt");
std::fs::write(
&txt,
format!(
"{{\"type\":\"user\",\"uuid\":\"u1\",\"timestamp\":\"2026-06-07T05:00:00.000Z\",\"message\":{{\"role\":\"user\",\"content\":\"hi\"}}}}\n{{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"{bx}\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}}\n"
),
)
.unwrap();
assert_eq!(
clone_origin(&clone, bx),
None,
"a non-jsonl decoy never becomes the origin"
);
std::fs::remove_file(&txt).unwrap();
let refr = root.join("s2.jsonl");
std::fs::write(
&refr,
format!(
"{{\"type\":\"user\",\"uuid\":\"u1\",\"timestamp\":\"2026-06-07T05:00:00.000Z\",\"message\":{{\"role\":\"user\",\"content\":\"hi\"}}}}\n{{\"type\":\"system\",\"subtype\":\"compact_boundary\",\"uuid\":\"77777777-8888-4999-8aaa-bbbbbbbbbbbb\",\"logicalParentUuid\":\"{bx}\",\"timestamp\":\"2026-06-07T05:10:00.000Z\"}}\n"
),
)
.unwrap();
assert_eq!(
clone_origin(&clone, bx),
None,
"a boundary that only references the uuid is not the carrier"
);
std::fs::remove_file(&refr).unwrap();
std::fs::remove_file(&clone).ok();
}
#[test]
fn checkpoint_close_reads_the_sum_and_refuses_other_types() {
let line = br#"{"type":"cost-state","sessionId":"s","totalCostUSD":0.5,"totalDuration":60000,"startTime":1780808940000,"modelUsage":{},"hasUnknownModelCost":false}"#;
assert_eq!(
checkpoint_close_ms(line),
Some(1_780_809_000_000),
"the close instant is startTime + totalDuration"
);
assert_eq!(
checkpoint_close_ms(
br#"{"type":"user","totalDuration":60000,"startTime":1780808940000,"message":{"role":"user","content":"x"}}"#
),
None
);
assert_eq!(
checkpoint_close_ms(br#"{"type":"cost-state","startTime":1780808940000}"#),
None
);
assert_eq!(checkpoint_close_ms(b"not json"), None);
}
#[test]
fn cleared_from_origin_takes_the_nearest_checkpoint_in_window() {
let dir = std::env::temp_dir().join(format!(
"csift-clear-{}-{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&dir).unwrap();
let wrapper = "2026-06-07T05:10:00.000Z";
let w = crate::timez::epoch_ms(wrapper).unwrap();
let checkpoint = |close: i64| {
format!(
r#"{{"type":"cost-state","sessionId":"s","totalCostUSD":0.1,"totalDuration":1000,"startTime":{},"modelUsage":{{}},"hasUnknownModelCost":false}}"#,
close - 1000
)
};
let near = dir.join("11111111-2222-4333-8444-555566667777.jsonl");
std::fs::write(
&near,
format!("{}\n{}\n", checkpoint(w - 1500), checkpoint(w - 7)),
)
.unwrap();
let far = dir.join("22222222-2222-4333-8444-555566667777.jsonl");
std::fs::write(&far, format!("{}\n", checkpoint(w - 90_000))).unwrap();
let cleared = dir.join("33333333-2222-4333-8444-555566667777.jsonl");
std::fs::write(&cleared, "{}\n").unwrap();
let join = cleared_from_origin(&cleared, wrapper);
assert_eq!(
join.cleared_from.as_deref(),
Some("11111111-2222-4333-8444-555566667777")
);
assert_eq!(
join.distance_ms,
Some(7),
"the nearest LINE wins, not the first"
);
assert!(!join.after, "this checkpoint closes before the wrapper");
assert!(join.candidates.is_empty());
std::fs::write(&near, format!("{}\n", checkpoint(w + 164))).unwrap();
std::fs::remove_file(&far).unwrap();
let after = cleared_from_origin(&cleared, wrapper);
assert_eq!(after.distance_ms, Some(164));
assert!(after.after, "the checkpoint closes after the wrapper");
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn cleared_from_origin_never_joins_a_file_to_itself() {
let dir = std::env::temp_dir().join(format!(
"csift-clear-self-{}-{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&dir).unwrap();
let wrapper = "2026-06-07T05:10:00.000Z";
let w = crate::timez::epoch_ms(wrapper).unwrap();
let checkpoint = |close: i64| {
format!(
r#"{{"type":"cost-state","sessionId":"s","totalCostUSD":0.1,"totalDuration":1000,"startTime":{},"modelUsage":{{}},"hasUnknownModelCost":false}}"#,
close - 1000
)
};
let cleared = dir.join("44444444-2222-4333-8444-555566667777.jsonl");
std::fs::write(&cleared, format!("{}\n", checkpoint(w - 5))).unwrap();
let join = cleared_from_origin(&cleared, wrapper);
assert!(
join.cleared_from.is_none() && join.candidates.is_empty(),
"a transcript is never joined to itself: {join:?}"
);
assert!(join.distance_ms.is_none(), "{join:?}");
std::fs::write(dir.join("notes.txt"), format!("{}\n", checkpoint(w - 1))).unwrap();
assert!(
cleared_from_origin(&cleared, wrapper).distance_ms.is_none(),
"only a .jsonl sibling is a candidate"
);
let sib = dir.join("55555555-2222-4333-8444-555566667777.jsonl");
std::fs::write(&sib, format!("{}\n", checkpoint(w - 9))).unwrap();
let joined = cleared_from_origin(&cleared, wrapper);
assert_eq!(
joined.cleared_from.as_deref(),
Some("55555555-2222-4333-8444-555566667777"),
"the self file and the decoy are excluded, not the whole directory"
);
assert_eq!(joined.distance_ms, Some(9), "the .txt decoy never wins");
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn cleared_from_origin_keeps_the_first_line_of_an_equal_distance_pair() {
let dir = std::env::temp_dir().join(format!(
"csift-clear-tie-{}-{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&dir).unwrap();
let wrapper = "2026-06-07T05:10:00.000Z";
let w = crate::timez::epoch_ms(wrapper).unwrap();
let checkpoint = |close: i64| {
format!(
r#"{{"type":"cost-state","sessionId":"s","totalCostUSD":0.1,"totalDuration":1000,"startTime":{},"modelUsage":{{}},"hasUnknownModelCost":false}}"#,
close - 1000
)
};
let cleared = dir.join("66666666-2222-4333-8444-555566667777.jsonl");
std::fs::write(&cleared, "{}\n").unwrap();
let sib = dir.join("77777777-2222-4333-8444-555566667777.jsonl");
std::fs::write(
&sib,
format!("{}\n{}\n", checkpoint(w - 40), checkpoint(w + 40)),
)
.unwrap();
let join = cleared_from_origin(&cleared, wrapper);
assert_eq!(join.distance_ms, Some(40));
assert!(
!join.after,
"the first equidistant line decides the direction: {join:?}"
);
std::fs::write(&sib, format!("{}\n", checkpoint(w))).unwrap();
let exact = cleared_from_origin(&cleared, wrapper);
assert_eq!(exact.distance_ms, Some(0));
assert!(!exact.after, "a zero distance is not `after`: {exact:?}");
std::fs::remove_dir_all(&dir).ok();
}