use super::{chat, commit, seat, step, steps};
use crate::git_tree::Framing;
use crate::rail::{Rail, build, pin};
use crate::steps_view::{StepSummary, StepsView};
use crate::transcript::{Block, Entry, EntryKind, Transcript, Usage};
fn entry(name: &str, kind: EntryKind) -> Entry {
Entry {
name: name.to_owned(),
raw: b"x".to_vec(),
kind,
}
}
fn delivered(name: &str) -> Entry {
entry(
name,
EntryKind::Delivered {
sender: "user".to_owned(),
sender_name: None,
epitaph: None,
body: "hi".to_owned(),
},
)
}
fn spoke(name: &str) -> Entry {
entry(
name,
EntryKind::Model {
model_id: "opus".to_owned(),
blocks: vec![Block::ToolUse {
id: "t1".to_owned(),
name: "bash".to_owned(),
input_summary: String::new(),
}],
usage: Usage::new(),
},
)
}
fn tool(name: &str) -> Entry {
entry(
name,
EntryKind::ToolResult {
tool_use_id: "t1".to_owned(),
content: "ok".to_owned(),
is_error: false,
},
)
}
fn framed(seq: &str, oid: &str, framing: Framing) -> StepSummary {
StepSummary {
framing,
..step(seq, Some(oid), 0)
}
}
fn spine(tx: &Transcript, view: StepsView) -> Rail {
build("root", &[], &view, tx, &[])
}
#[test]
fn a_tool_loop_gives_every_call_its_own_rule_not_the_next_drains() {
let tx = Transcript {
entries: vec![
delivered("001-user.md"),
spoke("002-opus.json"),
tool("003-tool.json"),
spoke("004-opus.json"),
tool("005-tool.json"),
spoke("006-opus.json"),
delivered("007-user.md"),
spoke("008-opus.json"),
],
};
let rail = spine(
&tx,
steps(vec![
step("001", Some("aaaa1111"), 0),
step("002", Some("bbbb2222"), 0),
step("003", Some("cccc3333"), 0),
step("004", Some("dddd4444"), 0),
]),
);
let rules = rail.rules();
assert_eq!(rules.get("tx/001-user.md#0"), Some(&0));
assert_eq!(rules.get("tx/003-tool.json#0"), Some(&1));
assert_eq!(rules.get("tx/005-tool.json#0"), Some(&2));
assert_eq!(rules.get("tx/007-user.md#0"), Some(&3));
assert_eq!(rules.len(), 4, "one rule per step: {rules:?}");
}
#[test]
fn two_drains_with_no_call_between_them_share_one_rule() {
let tx = Transcript {
entries: vec![
delivered("001-user.md"),
spoke("002-opus.json"),
delivered("003-user.md"),
delivered("004-peer.md"),
spoke("005-opus.json"),
],
};
let rail = spine(
&tx,
steps(vec![
step("001", Some("aaaa1111"), 0),
step("002", Some("bbbb2222"), 0),
]),
);
let rules = rail.rules();
assert_eq!(rules.len(), 2, "{rules:?}");
assert_eq!(rules.get("tx/003-user.md#0"), Some(&1));
}
#[test]
fn the_pin_cuts_to_what_that_call_read() {
let tx = chat(3);
let rail = spine(
&tx,
steps(vec![
step("001", Some("aaaa1111"), 5),
step("002", Some("bbbb2222"), 7),
step("003", Some("cccc3333"), 9),
]),
);
assert_eq!(pin(&rail, Some(0)).map(|p| p.cut), Some(1));
assert_eq!(pin(&rail, Some(1)).map(|p| p.cut), Some(3));
let second = crate::rail::transcript_as_of(&tx, 3);
let names: Vec<&str> = second.entries.iter().map(|e| e.name.as_str()).collect();
assert_eq!(names, ["001-user.md", "002-opus.json", "003-user.md"]);
assert_eq!(seat(1), "tx/003-user.md#0");
}
#[test]
fn the_running_call_marks_the_tail_of_the_chat() {
let tx = Transcript {
entries: vec![
delivered("001-user.md"),
spoke("002-opus.json"),
tool("003-tool.json"),
],
};
let rail = spine(
&tx,
steps(vec![
step("001", Some("aaaa1111"), 0),
framed("002", "bbbb2222", Framing::Killed),
]),
);
assert_eq!(rail.rules().get("tx/003-tool.json#0"), Some(&1));
assert_eq!(pin(&rail, Some(1)).map(|p| p.cut), Some(3));
}
#[test]
fn a_superseded_call_that_sealed_nothing_has_no_seat() {
let tx = Transcript {
entries: vec![
delivered("001-user.md"),
delivered("002-user.md"),
spoke("003-opus.json"),
],
};
let rail = spine(
&tx,
steps(vec![
framed("001", "aaaa1111", Framing::Killed),
step("002", Some("bbbb2222"), 0),
]),
);
let rules = rail.rules();
assert_eq!(rules.len(), 1, "{rules:?}");
assert_eq!(rules.get("tx/001-user.md#0"), Some(&1));
assert!(pin(&rail, Some(0)).is_none(), "no seat, no pin");
}
#[test]
fn a_step_the_chat_has_not_caught_up_with_takes_no_seat() {
let empty = spine(
&Transcript::default(),
steps(vec![step("001", Some("aaaa1111"), 0)]),
);
assert!(empty.rules().is_empty());
let ahead = spine(
&chat(1),
steps(vec![
step("001", Some("aaaa1111"), 0),
step("002", Some("bbbb2222"), 0),
]),
);
let rules = ahead.rules();
assert_eq!(rules.len(), 1, "{rules:?}");
assert_eq!(rules.get(&seat(0)), Some(&0));
assert_eq!(commit("aaaa1111", 1).oid, "aaaa1111");
}