Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ part of the review and are sent whether or not its folder is listed.
## Inside Herdr

Install [Herdr Annotate](https://github.com/plannotator/herdr-annotate); it bundles this binary,
opens it in a pane with `prefix+o` (folder) or `prefix+shift+o` (agent's last reply) or by
Ctrl-clicking a `file://…md` link, and the header button sends the review straight back to
the agent as its next message: `Send 3 new ▸ claude in w1:p2 (E)`. Folder reviews show
opens it in a pane with `prefix+o` (folder) or `prefix+shift+o` (agent's last reply), and the
header button sends the review straight back to the agent as its next message:
`Send 3 new ▸ claude in w1:p2 (E)`. Ctrl-clicking a `file://…md` link opens it in Annotate only
if you install the add-on: `herdr plugin install plannotator/herdr-annotate/links`. Folder reviews show
`Send 3 new across 2 files` and send one combined feedback message.

```toml
Expand Down
5 changes: 4 additions & 1 deletion crates/plannotator-tui-hosts/tests/claude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,8 @@ fn a_dangling_parent_makes_the_chain_untrusted_and_file_order_applies() {

#[test]
fn garbage_input_yields_no_messages_rather_than_an_error() {
assert!(parse_messages("not json\n{\"type\":\"progress\"}\n", 3).is_empty());
assert_eq!(
parse_messages("not json\n{\"type\":\"progress\"}\n", 3),
[] as [plannotator_tui_hosts::Message; 0]
);
}
2 changes: 1 addition & 1 deletion crates/plannotator-tui-hosts/tests/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ fn when_every_turn_has_completed_the_whole_thread_counts_newest_first() {

#[test]
fn an_unknown_thread_has_no_files() {
assert!(find_transcripts(home(), Some("nope")).is_empty());
assert_eq!(find_transcripts(home(), Some("nope")), [] as [std::path::PathBuf; 0]);
}
2 changes: 1 addition & 1 deletion crates/plannotator-tui-hosts/tests/exact_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fn exact_file_host_ids_prefer_the_current_windows_path_bucket() {
&codex_home.join("sessions/2026/08/31").join(format!("rollout-2026-08-31T10-00-00-{OTHER_ID}.jsonl")),
);
assert_eq!(codex::find_transcripts_by_id(&codex_home, ID).expect("codex"), vec![codex_file]);
assert!(codex::find_transcripts_by_id(&codex_home, "missing").expect("miss").is_empty());
assert_eq!(codex::find_transcripts_by_id(&codex_home, "missing").expect("miss"), [] as [PathBuf; 0]);

let copilot_home = root.join("copilot root");
let copilot_session = copilot_home.join("session-state").join(ID);
Expand Down
2 changes: 1 addition & 1 deletion crates/plannotator-tui-hosts/tests/pi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fn tool_calls_tool_results_and_thinking_are_not_messages() {

#[test]
fn an_unreconstructable_chain_yields_nothing_rather_than_the_wrong_messages() {
assert!(parse_messages(&fixture("pi-dangling.jsonl"), 25).is_empty());
assert_eq!(parse_messages(&fixture("pi-dangling.jsonl"), 25), [] as [plannotator_tui_hosts::Message; 0]);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion crates/plannotator-tui/src/app/feedback/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ fn reply_reviews_keep_sending_the_whole_transient_review() {
assert!(calls[1].contains("> A") && calls[1].contains("> B"));
press(&mut app, 'F');
assert_eq!(app.open.store.len(), 2);
assert!(app.open.store.archived().is_empty());
assert_eq!(app.open.store.archived(), []);
assert!(app.open.store.is_transient());
assert!(!draw(&mut app, 80, 24).contains("Review \u{25be}"), "a reply review has no Review menu");
std::fs::remove_dir_all(root).expect("cleanup");
Expand Down
2 changes: 1 addition & 1 deletion crates/plannotator-tui/src/app/review/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn finish_undo_and_restore_keep_pending_edits_and_preserve_sent_status() {
click(&mut app, undo);
assert_eq!(app.open.store.len(), 3);
assert_eq!(app.send_count(), 2, "undo did not make A pending");
assert!(app.open.store.archived().is_empty());
assert_eq!(app.open.store.archived(), []);

press(&mut app, 'F');
reopen(&mut app);
Expand Down
4 changes: 2 additions & 2 deletions crates/plannotator-tui/src/store/review/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ fn archiving_keeps_pending_notes_and_restores_complete_annotations_and_history()
let restored = store.annotations.iter().find(|a| a.id == original.id).expect("same id");
assert_eq!(restored, &original);
assert!(!store.is_pending(restored), "restoring must not resend it");
assert!(store.archived().is_empty());
assert_eq!(store.archived(), []);
assert_eq!(store.deliveries, history);
std::fs::remove_dir_all(root).expect("cleanup");
}
Expand All @@ -208,7 +208,7 @@ fn archive_and_restore_failures_leave_memory_and_disk_intact() {
std::fs::create_dir(&blocked_tmp).expect("block temporary file");
assert!(store.archive_sent().is_err());
assert_eq!(store.len(), 1);
assert!(store.archived().is_empty());
assert_eq!(store.archived(), []);
assert_eq!(std::fs::read(&location.record).expect("record"), before);
std::fs::remove_dir(&blocked_tmp).expect("unblock");

Expand Down
4 changes: 2 additions & 2 deletions crates/plannotator-tui/tests/codex_pid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fn ambiguous_pid_does_not_read_the_newest_session() {
let process = HoldingProcess::new(&fixture.selected, Some(&fixture.other));
let out = fixture.command(process.0.id()).output().expect("runs");
assert!(out.status.success(), "--print reports discovery failures without aborting its caller");
assert!(out.stdout.is_empty());
assert_eq!(out.stdout, [] as [u8; 0]);
assert!(String::from_utf8_lossy(&out.stderr).contains("2 open transcripts"));
}

Expand All @@ -156,7 +156,7 @@ fn pid_without_an_open_transcript_does_not_read_another_session() {
let process = HoldingProcess::new(Path::new("/dev/null"), None);
let out = fixture.command(process.0.id()).output().expect("runs");
assert!(out.status.success());
assert!(out.stdout.is_empty());
assert_eq!(out.stdout, [] as [u8; 0]);
assert!(String::from_utf8_lossy(&out.stderr).contains("0 open transcripts"));
}

Expand Down
2 changes: 1 addition & 1 deletion crates/plannotator-tui/tests/last.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ fn print_never_fails_the_caller_when_nothing_is_found() {
let missing = fixtures().join("does-not-exist.jsonl");
let out = bin().args(["last", "--session"]).arg(&missing).arg("--print").output().expect("runs");
assert!(out.status.success(), "exit 0 is the contract");
assert!(out.stdout.is_empty());
assert_eq!(out.stdout, [] as [u8; 0]);
assert!(String::from_utf8_lossy(&out.stderr).contains("does-not-exist.jsonl"));
}

Expand Down
2 changes: 1 addition & 1 deletion crates/plannotator-tui/tests/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fn an_action_without_a_focused_pane_reads_nothing() {

assert!(!out.status.success());
assert!(String::from_utf8_lossy(&out.stderr).contains("no focused pane to read"));
assert!(calls(&fake).is_empty());
assert_eq!(calls(&fake), [] as [Value; 0]);
std::fs::remove_dir_all(root).expect("cleanup");
}

Expand Down
Loading