use std::{
fs,
io::Write as _,
os::unix::fs::PermissionsExt,
path::{Path, PathBuf},
process::{Command, Stdio},
thread,
time::{Duration, Instant},
};
use serde_json::{Value, json};
use tempfile::TempDir;
fn brgr() -> &'static str {
env!("CARGO_BIN_EXE_brgr")
}
/// brgr for a test. Debug builds trust a faked `HERDR_PANE_ID` only with this
/// variable: a test's own process chain never runs inside the pane it claims.
fn brgr_command() -> Command {
let mut command = Command::new(brgr());
command.env("BRGR_TEST_TRUST_HERDR_PANE", "1");
command
}
fn run(home: &Path, args: &[&str], envs: &[(&str, &str)]) -> std::process::Output {
let mut command = brgr_command();
command.arg("--home").arg(home).arg("--json").args(args);
// These fixtures certify one-shot process behavior. Native TUI tests opt
// into the pane fixture below; a production default call is tested separately.
if matches!(args.first(), Some(&"run" | &"revise"))
&& !args.contains(&"local.omp-herdr")
&& !(envs.iter().any(|(name, _)| *name == "HERDR_PANE_ID")
&& (args.contains(&"local.claude-code") || args.contains(&"local.opencode")))
{
command.arg("--headless");
}
command.env_remove("CODEX_THREAD_ID");
command.env_remove("CLAUDE_CODE_SESSION_ID");
for name in [
"HERDR_ENV",
"HERDR_PANE_ID",
"HERDR_TAB_ID",
"HERDR_WORKSPACE_ID",
"HERDR_BIN_PATH",
"HERDR_SOCKET_PATH",
"HERDR_SESSION",
"HERDR_PLUGIN_ID",
] {
command.env_remove(name);
}
// A Claude Code owner is identified by its own session variable instead.
if !envs
.iter()
.any(|(name, _)| *name == "CLAUDE_CODE_SESSION_ID")
{
command.env("BRGR_SESSION_ID", "fixture-session");
}
for (name, value) in envs {
command.env(name, value);
}
command.output().unwrap()
}
fn json_output(output: &std::process::Output) -> Value {
assert!(
output.status.success(),
"command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
serde_json::from_slice(&output.stdout).unwrap()
}
fn add_fixture(home: &Path, fixture: &Path, scratch: &Path) {
fs::create_dir_all(scratch).unwrap();
let receipt = json_output(&run(
home,
&[
"harness",
"add",
fixture.to_str().unwrap(),
"--workspace",
scratch.to_str().unwrap(),
"--prompt",
"BRGR_FIXTURE_OK",
],
&[],
));
assert!(receipt["scratch_result_digest"].as_str().is_some());
}
/// Fails unless brgr runs it the way the Devin recipe promises: print mode,
/// workspace trust bypassed, and — with no level requested — the full level,
/// which Devin calls `dangerous`.
const DEVIN_FIXTURE: &str = r#"#!/bin/sh
case "$1" in
--version) echo 'devin 3000.fixture'; exit 0;;
--help)
printf '%s\n' '--prompt-file <FILE>' '-p, --print [<PROMPT>]' '--permission-mode <PERMISSION_MODE>' '--respect-workspace-trust [<RESPECT_WORKSPACE_TRUST>]' '--model <MODEL>'
exit 0;;
models)
test "$2" = list && test "$3" = --format && test "$4" = json || exit 2
echo '{"families":[{"slug":"swe-1.6"}]}'
exit 0;;
esac
prompt_file=
print_mode=0
full_mode=0
trust_bypassed=0
while test "$#" -gt 0; do
case "$1" in
--prompt-file) shift; prompt_file=$1;;
-p|--print) print_mode=1;;
--permission-mode) shift; test "$1" = dangerous || exit 3; full_mode=1;;
--respect-workspace-trust) shift; test "$1" = false || exit 4; trust_bypassed=1;;
*) exit 5;;
esac
shift
done
test "$print_mode" = 1 || exit 6
test "$full_mode" = 1 || exit 7
test "$trust_bypassed" = 1 || exit 8
test -f "$prompt_file" || exit 10
/bin/cat "$prompt_file"
"#;
/// The gjc fixture harness every contract test registers.
fn gjc_fixture() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../testdata/fixtures/gjc")
.canonicalize()
.unwrap()
}
/// A control home with the gjc fixture registered, and an empty workspace.
fn gjc_home() -> (TempDir, PathBuf, PathBuf) {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
fs::create_dir_all(&workspace).unwrap();
add_fixture(&home, &gjc_fixture(), &temp.path().join("scratch"));
(temp, home, workspace)
}
/// An echo harness returns the prompt it was given. The objective must still be
/// its exact first line — some harnesses key on it, OMP on `FROM CODEX` — and
/// the owner-messaging brief must follow, since every worker may ask.
fn assert_worker_prompt(echoed: &str, objective: &str) {
assert_eq!(echoed.lines().next(), Some(objective), "{echoed}");
assert!(echoed.contains("BRGR OWNER MESSAGES"), "{echoed}");
assert!(
echoed.contains("Child delegation is not enabled"),
"{echoed}"
);
}
fn write_devin_fixture(executable: &Path) {
fs::write(executable, DEVIN_FIXTURE).unwrap();
fs::set_permissions(executable, fs::Permissions::from_mode(0o700)).unwrap();
}
#[test]
fn plugin_board_shows_candidate_then_explicit_decision_without_prompt_text() {
let (_temp, home, workspace) = gjc_home();
let envs = [
("BRGR_OWNER_ID", "codex:board-test"),
("HERDR_ENV", "1"),
("HERDR_PLUGIN_ID", "brgr"),
];
let created = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&envs,
));
let task_id = created["task_id"].as_str().unwrap();
let board = run(&home, &["plugin", "board", "--once"], &envs);
assert!(board.status.success());
let board_text = String::from_utf8(board.stdout).unwrap();
assert!(board_text.contains(&task_id[..8]));
assert!(board_text.contains(" work "));
assert!(board_text.contains("candidate"));
assert!(!board_text.contains("BRGR_FIXTURE_OK"));
json_output(&run(
&home,
&["accept", task_id, "--reason", "fixture verified"],
&envs,
));
let decided = run(&home, &["plugin", "board", "--once"], &envs);
assert!(decided.status.success());
assert!(
String::from_utf8(decided.stdout)
.unwrap()
.contains("accepted")
);
let decided_text =
String::from_utf8(run(&home, &["plugin", "board", "--once"], &envs).stdout).unwrap();
assert!(!decided_text.contains("BRGR_FIXTURE_OK"));
assert!(!decided_text.contains("fixture verified"));
}
#[test]
fn plugin_board_refresh_failure_is_visible_without_mutating_tasks() {
let (_temp, home, workspace) = gjc_home();
let envs = [
("BRGR_OWNER_ID", "codex:board-fail"),
("HERDR_ENV", "1"),
("HERDR_PLUGIN_ID", "brgr"),
];
let created = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&envs,
));
let task_id = created["task_id"].as_str().unwrap();
let store = home.join("store").join("brgr.sqlite3");
let original = fs::read(&store).unwrap();
fs::write(&store, b"not a sqlite database").unwrap();
let failed = run(&home, &["plugin", "board", "--once"], &envs);
assert!(!failed.status.success());
let stderr = String::from_utf8_lossy(&failed.stderr);
assert!(stderr.contains("board refresh failed"));
assert!(stderr.contains("No tasks were mutated or reconciled"));
assert!(!stderr.contains("BRGR_FIXTURE_OK"));
fs::write(&store, original).unwrap();
let restored = run(&home, &["plugin", "board", "--once"], &envs);
assert!(restored.status.success());
let restored_text = String::from_utf8(restored.stdout).unwrap();
assert!(restored_text.contains(&task_id[..8]));
assert!(restored_text.contains("candidate"));
}
#[test]
fn plugin_codex_bridge_runs_a_fixture_outside_the_codex_process() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex-home");
let workspace = temp.path().join("work");
let outside = temp.path().join("outside");
let fake_bin = temp.path().join("bin");
let output_path = temp.path().join("candidate.json");
let codex_args_path = temp.path().join("codex-args.txt");
let denied_harness_path = temp.path().join("denied-harness.txt");
let denied_workspace_path = temp.path().join("denied-workspace.txt");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&outside).unwrap();
fs::create_dir_all(&fake_bin).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let fake_codex = fake_bin.join("codex");
fs::write(
&fake_codex,
"#!/bin/sh\nprintf '%s\\n' \"$@\" > \"$BRGR_TEST_CODEX_ARGS\"\nexport CODEX_THREAD_ID=bridge-test\n\"$BRGR_BIN\" --json harness add /bin/echo --presentation-only > /dev/null 2> \"$BRGR_TEST_DENIED_HARNESS\"\nprintf '%s\\n' \"$?\" >> \"$BRGR_TEST_DENIED_HARNESS\"\n\"$BRGR_BIN\" --json --headless run ESCAPE --harness local.gjc --workspace \"$BRGR_TEST_OUTSIDE\" --foreground > /dev/null 2> \"$BRGR_TEST_DENIED_WORKSPACE\"\nprintf '%s\\n' \"$?\" >> \"$BRGR_TEST_DENIED_WORKSPACE\"\nexec \"$BRGR_BIN\" --json --headless run BRGR_FIXTURE_OK --harness local.gjc --criterion 'artifact text equals BRGR_FIXTURE_OK' --workspace \"$BRGR_TEST_WORKSPACE\" --foreground > \"$BRGR_TEST_OUTPUT\"\n",
)
.unwrap();
fs::set_permissions(&fake_codex, fs::Permissions::from_mode(0o700)).unwrap();
let path = std::env::join_paths(
std::iter::once(fake_bin.clone())
.chain(std::env::split_paths(&std::env::var_os("PATH").unwrap())),
)
.unwrap();
let context = json!({"workspace_id": "w1", "workspace_cwd": workspace});
let launched = brgr_command()
.args(["plugin", "codex"])
.env("HERDR_ENV", "1")
.env("HERDR_PLUGIN_ID", "brgr")
.env("HERDR_PLUGIN_CONTEXT_JSON", context.to_string())
.env("BRGR_HOME", &home)
.env("CODEX_HOME", &codex_home)
.env("BRGR_BIN", brgr())
.env("BRGR_TEST_WORKSPACE", &workspace)
.env("BRGR_TEST_OUTSIDE", &outside)
.env("BRGR_TEST_OUTPUT", &output_path)
.env("BRGR_TEST_CODEX_ARGS", &codex_args_path)
.env("BRGR_TEST_DENIED_HARNESS", &denied_harness_path)
.env("BRGR_TEST_DENIED_WORKSPACE", &denied_workspace_path)
.env("PATH", path)
.env_remove("CODEX_THREAD_ID")
.env_remove("BRGR_SESSION_ID")
.output()
.unwrap();
assert!(
launched.status.success(),
"plugin Codex bridge failed: {}",
String::from_utf8_lossy(&launched.stderr)
);
let codex_args = fs::read_to_string(codex_args_path).unwrap();
assert!(codex_args.contains("shell_environment_policy.set.PATH="));
assert!(codex_args.contains("shell_environment_policy.set.BRGR_PLUGIN_BRIDGE_DIR="));
let args = codex_args.lines().collect::<Vec<_>>();
let add_dir = args
.windows(2)
.find_map(|pair| (pair[0] == "--add-dir").then_some(pair[1]))
.expect("Codex launch must expose only the private bridge directory");
assert_ne!(Path::new(add_dir), home);
assert!(
!Path::new(add_dir).exists(),
"bridge tempdir must close with Codex"
);
let denied_harness = fs::read_to_string(&denied_harness_path).unwrap();
assert!(denied_harness.contains("unavailable through the Herdr host bridge"));
assert_ne!(denied_harness.lines().last(), Some("0"));
let denied_workspace = fs::read_to_string(&denied_workspace_path).unwrap();
assert!(denied_workspace.contains("outside the selected Herdr workspace"));
assert_ne!(denied_workspace.lines().last(), Some("0"));
assert_eq!(
brgr_registry::Registry::open(home.join("registry"))
.unwrap()
.registered_harness_ids()
.unwrap(),
vec!["local.gjc"]
);
let candidate: Value = serde_json::from_slice(&fs::read(&output_path).unwrap()).unwrap();
assert_eq!(candidate["outcome"], "candidate");
let task = candidate["task_id"].as_str().unwrap();
let result = json_output(&run(
&home,
&["result", task],
&[
("CODEX_THREAD_ID", "bridge-test"),
("BRGR_SESSION_ID", "bridge-test"),
],
));
assert_eq!(result["artifacts"][0]["text"], "BRGR_FIXTURE_OK");
assert!(!fs::read_dir(&home).unwrap().any(|entry| {
entry
.unwrap()
.file_name()
.to_string_lossy()
.starts_with("plugin-bridge-")
}));
}
#[test]
fn plugin_codex_uses_pinned_workspace_not_live_plugin_cwd() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex-home");
let project = temp.path().join("project");
let plugin_checkout = temp.path().join("plugin-checkout");
let fake_bin = temp.path().join("bin");
let codex_args_path = temp.path().join("codex-args.txt");
fs::create_dir_all(&project).unwrap();
fs::create_dir_all(&plugin_checkout).unwrap();
fs::create_dir_all(&fake_bin).unwrap();
let fake_codex = fake_bin.join("codex");
fs::write(
&fake_codex,
"#!/bin/sh\nprintf '%s\\n' \"$@\" > \"$BRGR_TEST_CODEX_ARGS\"\n",
)
.unwrap();
fs::set_permissions(&fake_codex, fs::Permissions::from_mode(0o700)).unwrap();
let path = std::env::join_paths(
std::iter::once(fake_bin.clone())
.chain(std::env::split_paths(&std::env::var_os("PATH").unwrap())),
)
.unwrap();
let context = json!({
"workspace_id": "w1",
"workspace_cwd": plugin_checkout,
"focused_pane_cwd": plugin_checkout
});
let launched = brgr_command()
.args(["plugin", "codex"])
.env("HERDR_ENV", "1")
.env("HERDR_PLUGIN_ID", "brgr")
.env("HERDR_PLUGIN_CONTEXT_JSON", context.to_string())
.env("BRGR_PLUGIN_WORKSPACE_CWD", &project)
.env("BRGR_HOME", &home)
.env("CODEX_HOME", &codex_home)
.env("BRGR_TEST_CODEX_ARGS", &codex_args_path)
.env("PATH", path)
.env_remove("CODEX_THREAD_ID")
.env_remove("BRGR_SESSION_ID")
.output()
.unwrap();
assert!(
launched.status.success(),
"{}",
String::from_utf8_lossy(&launched.stderr)
);
let args = fs::read_to_string(codex_args_path).unwrap();
let mut found_cwd = false;
let mut lines = args.lines();
while let Some(line) = lines.next() {
if line == "-C" {
assert_eq!(lines.next(), Some(project.to_str().unwrap()));
found_cwd = true;
break;
}
}
assert!(found_cwd, "{args}");
assert!(args.contains("shell_environment_policy.set.PATH="));
assert!(args.contains("shell_environment_policy.set.BRGR_HOME="));
}
#[test]
fn plugin_entrypoints_fail_closed_without_herdr_host() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let board = run(&home, &["plugin", "board", "--once"], &[]);
assert!(!board.status.success());
assert!(String::from_utf8_lossy(&board.stderr).contains("brgr Herdr plugin host"));
let open = run(&home, &["plugin", "open", "--no-focus"], &[]);
assert!(!open.status.success());
assert!(String::from_utf8_lossy(&open.stderr).contains("brgr Herdr plugin host"));
}
/// A task placed in a Herdr worker pane is claimed only once Herdr has opened
/// the pane and started `brgr plugin worker` in it. Another brgr command in the
/// meantime must not record it lost after the detached supervisor's five-second
/// window; it gets a minute.
#[test]
fn explicit_headless_does_not_open_a_second_worker_pane() {
let fixture = pane_fixture();
json_output(&run(
&fixture.home,
&["config", "set-auto-worker-pane", "true"],
&[],
));
let ran = fixture.run(&["--headless"]);
assert_eq!(ran["outcome"], "candidate");
assert!(!fixture.state("calls.log").contains("pane split"));
}
#[test]
fn native_worker_placement_uses_config_and_reaches_owner_decision() {
let fixture = pane_fixture();
json_output(&run(
&fixture.home,
&["config", "set-worker-placement", "tab"],
&[],
));
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate");
let calls = fixture.state("calls.log");
assert!(calls.contains("tab create --workspace w9"), "{calls}");
assert!(!calls.contains("pane split"));
assert!(calls.contains("--no-focus"));
assert!(fixture.state("closed").is_empty());
json_output(&run(
&fixture.home,
&["accept", ran["task_id"].as_str().unwrap()],
&[("BRGR_OWNER_ID", "codex:pane-test")],
));
// The decision closes the pane at once when the agent reads idle;
// otherwise the session loop closes it on its next poll.
let deadline = Instant::now() + Duration::from_secs(10);
while fixture.state("closed").trim() != "closed" && Instant::now() < deadline {
thread::sleep(Duration::from_millis(100));
}
assert_eq!(fixture.state("closed").trim(), "closed");
}
/// A worker that asks its owner, or tries to delegate, as its prompt says.
///
/// Runs as a real harness process, so what it can do is exactly what brgr gave
/// it — the identity in its environment and nothing the test simulates.
const ASKING_GJC_FIXTURE: &str = r#"#!/bin/sh
set -u
case "${1:-}" in
--version) echo 'gjc v-asking-fixture'; exit 0;;
--help)
printf '%s\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'
exit 0;;
esac
prompt_file=
for argument in "$@"; do
case "$argument" in @*) prompt_file=${argument#@};; esac
done
answer=NO_WORKER_IDENTITY
if test -n "${BRGR_PARENT_ATTEMPT_ID:-}"; then
if /usr/bin/grep -q '^ASK' "$prompt_file"; then
"$BRGR_BIN" --json message send "$BRGR_PARENT_TASK_ID" --to owner --kind question --body 'Which token?' >/dev/null
reply=$("$BRGR_BIN" --json message wait "$BRGR_PARENT_TASK_ID" --for worker --timeout-seconds 20)
reply_id=$(printf '%s\n' "$reply" | /usr/bin/sed -n 's/.*"message_id":"\([^"]*\)".*/\1/p')
"$BRGR_BIN" --json message ack "$BRGR_PARENT_TASK_ID" "$reply_id" --for worker >/dev/null
answer=$(printf '%s\n' "$reply" | /usr/bin/sed -n 's/.*"body":"\([^"]*\)".*/\1/p')
elif /usr/bin/grep -q '^SPAWN' "$prompt_file"; then
if "$BRGR_BIN" --json --headless run LEAF --harness local.gjc --workspace "$PWD" --foreground >/dev/null 2>"$PWD/../spawn.err"; then
answer=SPAWN_ALLOWED
elif /usr/bin/grep -q 'not started with delegation' "$PWD/../spawn.err"; then
answer=SPAWN_REFUSED
else
answer=SPAWN_FAILED_OTHERWISE
fi
fi
fi
printf '{"type":"message_end","message":{"role":"assistant","content":[{"type":"text","text":"%s"}]}}\n' "$answer"
printf '%s\n' '{"type":"agent_end","stopReason":"completed"}'
"#;
fn asking_home() -> (TempDir, PathBuf, PathBuf) {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(&executable, ASKING_GJC_FIXTURE).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
(temp, home, workspace)
}
/// A worker started by a plain `brgr run` can ask its owner and use the reply.
///
/// Before 2.4.0 the identity a worker needs to message was attached only when
/// the task was started with delegation, so the default route — the one the
/// orchestration skill uses — could not ask at all. The author's store held
/// zero messages across 57 tasks.
#[test]
fn a_worker_started_without_delegation_can_ask_its_owner() {
let (_temp, home, workspace) = asking_home();
let owner = [("BRGR_OWNER_ID", "codex:ask-test")];
let launched = json_output(&run(
&home,
&["run", "ASK", "--workspace", workspace.to_str().unwrap()],
&owner,
));
let task = launched["task_id"].as_str().unwrap();
let question = json_output(&run(
&home,
&[
"message",
"wait",
task,
"--for",
"owner",
"--timeout-seconds",
"20",
],
&owner,
));
assert_eq!(question["body"], "Which token?", "{question}");
let question_id = question["message_id"].as_str().unwrap();
json_output(&run(
&home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"reply",
"--reply-to",
question_id,
"--body",
"token-42",
],
&owner,
));
json_output(&run(
&home,
&["message", "ack", task, question_id, "--for", "owner"],
&owner,
));
json_output(&run(
&home,
&["wait", task, "--timeout-seconds", "20"],
&owner,
));
let result = json_output(&run(&home, &["result", task], &owner));
assert_eq!(result["artifacts"][0]["text"], "token-42", "{result}");
}
/// Messaging is for every worker; delegation stays a permission.
///
/// Giving every worker its identity also lets it name itself as a parent, so
/// the permission is checked at child admission against the parent's launch.
#[test]
fn a_worker_started_without_delegation_cannot_start_a_child() {
let (_temp, home, workspace) = asking_home();
let owner = [("BRGR_OWNER_ID", "codex:spawn-test")];
let result = json_output(&run(
&home,
&[
"run",
"SPAWN",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
let task = result["task_id"].as_str().unwrap();
let sealed = json_output(&run(&home, &["result", task], &owner));
assert_eq!(sealed["artifacts"][0]["text"], "SPAWN_REFUSED", "{sealed}");
let listed = json_output(&run(&home, &["status"], &owner));
assert_eq!(listed.as_array().map(Vec::len), Some(1), "{listed}");
}
/// A level the harness cannot honour is refused before anything runs, and a
/// configured cap binds a task that asked for nothing instead of being ignored.
///
/// Workers always run with full permissions. A lower level asked for by an old
/// script, a config value or a stored task is accepted and ignored, and the
/// commands that used to set a cap are gone.
#[test]
fn a_requested_permission_never_lowers_a_worker() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:permission-test")];
let ws = workspace.to_str().unwrap();
let output = run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
ws,
"--foreground",
"--permission",
"read-only",
],
&owner,
);
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
assert!(String::from_utf8_lossy(&output.stderr).contains("ignored"));
let ran = json_output(&output);
assert_eq!(ran["outcome"], "candidate", "{ran}");
assert_eq!(
store_task(&home, ran["task_id"].as_str().unwrap()).permission,
None
);
// The config key is accepted for old scripts and never applied.
let set = run(&home, &["config", "set", "permission", "read-only"], &owner);
assert!(set.status.success());
let ran = json_output(&run(
&home,
&["run", "BRGR_FIXTURE_OK", "--workspace", ws, "--foreground"],
&owner,
));
assert_eq!(ran["outcome"], "candidate", "{ran}");
assert_eq!(
store_task(&home, ran["task_id"].as_str().unwrap()).permission,
None
);
// There is no cap to set any more.
for command in ["set-max-permission", "clear-max-permission"] {
let gone = run(&home, &["config", command, "edits"], &owner);
assert!(!gone.status.success(), "{command} still exists");
}
}
fn store_task(home: &Path, task: &str) -> brgr_protocol::TaskSpec {
brgr_store::Store::open(home.join("store"))
.unwrap()
.task(task.parse().unwrap())
.unwrap()
}
/// A Claude Code stand-in for registration: it documents the recipe's flags and
/// answers a print-mode run.
const CLAUDE_FIXTURE: &str = r#"#!/bin/sh
case "$1" in
--version) echo '2.1.0 (Claude Code)'; exit 0;;
--help) printf '%s\n' '-p, --print' '--output-format <format>' '--model <model>' '--permission-mode <mode>' '--effort <level>'; exit 0;;
esac
echo CLAUDE_PRINT_MODE_OK
"#;
/// An `opencode` stand-in: its interactive command takes no effort flag, which
/// only `opencode run` accepts.
const OPENCODE_FIXTURE: &str = r#"#!/bin/sh
case "$1 $2" in
'--version '*) echo '1.18.0'; exit 0;;
'run --help') printf '%s\n' '--model' '--variant' '--agent' '--auto'; exit 0;;
'models '*) echo 'opencode/big-pickle'; exit 0;;
esac
echo OPENCODE_PRINT_MODE_OK
"#;
/// An `OpenCode` 2 stand-in: `--variant` is gone, and a run outside a private
/// server (`--standalone`) would keep going in the shared service after brgr
/// stops the client, so this one refuses it.
const OPENCODE_2_FIXTURE: &str = r#"#!/bin/sh
case "$1 $2" in
'--version '*) echo 'opencode v2.0.20'; exit 0;;
'run --help') printf '%s\n' ' --standalone Run with a private server instead of the background service' ' --model, -m string Model to use in the format provider/model#variant' ' --agent string Agent to use' ' --auto Auto-approve permissions that are not explicitly denied'; exit 0;;
esac
model=none standalone=no previous=
for arg in "$@"; do
test "$previous" = --model && model=$arg
test "$arg" = --standalone && standalone=yes
previous=$arg
done
test "$standalone" = yes || { echo 'ran in the shared background service' >&2; exit 7; }
echo "OPENCODE_2_MODEL=$model"
"#;
/// A Herdr stand-in. It opens a pane, starts an agent, and — standing in for
/// the agent too — writes the report named in the prompt, then goes from
/// working to idle. Every call and the pane's environment are recorded beside
/// it, since the pane runner does not pass test variables through.
const PANE_HERDR_FIXTURE: &str = r#"#!/bin/sh
d="$(/usr/bin/dirname "$0")/herdr-state"
/bin/mkdir -p "$d"
seal_report() {
/usr/bin/python3 - "$d/native-config.json" <<'PY'
import pathlib,json,subprocess,sys,os
c=json.loads(pathlib.Path(sys.argv[1]).read_text());e=dict(os.environ);e.update(c['environment']);revision=pathlib.Path(c['state']).name.split('-r')[1].split('.')[0]
p=subprocess.run([e['BRGR_BIN'],'--home',e['BRGR_HOME'],'__seal-report',e['BRGR_PARENT_TASK_ID'],revision],env=e,capture_output=True);assert p.returncode==0,p.stderr
PY
}
if [ "$1" = --session ]; then shift 2; fi
printf '%s\n' "$*" >> "$d/calls.log"
case "$1 $2" in
'pane split'|'tab create')
shift 2
while [ $# -gt 0 ]; do
case "$1" in
--env) printf '%s\n' "$2" >> "$d/pane-env"; shift 2;;
--cwd) printf '%s\n' "$2" > "$d/pane-cwd"; shift 2;;
*) shift;;
esac
done
echo '{"result":{"pane":{"pane_id":"w9:p2"}}}';;
'pane run')
/usr/bin/python3 - "$4" "$d" <<'PY'
import json,sys,shlex,pathlib,subprocess,os
config=json.loads(pathlib.Path(shlex.split(sys.argv[1])[-1]).read_text())
root=pathlib.Path(sys.argv[2]);pid=int(subprocess.check_output(['/bin/ps','-p',str(os.getppid()),'-o','ppid='],text=True).strip())
(root/'native-config.json').write_text(json.dumps(config))
birth=subprocess.check_output(['/bin/ps','-p',str(pid),'-o','lstart='],text=True).strip()
if (root/'start-fails').exists():
print('registered native executable could not start',file=sys.stderr);sys.exit(9)
pathlib.Path(config['state']).write_text(json.dumps({'pid':pid,'birth':birth,'phase':'running','exit_code':None}))
kind=pathlib.Path(config['executable']).name
(root/'start-args').write_text('\n'.join(['agent','start','--kind',kind,*config['argv']]))
(root/'start-attempts').write_text('native host started once\n')
(root/'state').write_text('blocked' if (root/'start-blocked').exists() else 'idle')
PY
test $? = 0 || exit 9
echo '{"result":{"type":"ok"}}';;
'pane layout')
w=$(/bin/cat "$d/caller-width" 2>/dev/null || echo 200)
printf '{"result":{"layout":{"panes":[{"pane_id":"w9:p1","rect":{"width":%s,"height":50}}]}}}\n' "$w";;
'pane get') echo '{"result":{"pane":{"pane_id":"w9:p2","terminal_id":"terminal-native-fixture"}}}';;
'pane send-text') printf '%s' "$4" > "$d/paste"; echo '{}';;
'pane send-keys')
/bin/rm -f "$d/start-blocked"
if [ -s "$d/paste" ] && [ -e "$d/slow-seal" ]; then
report=$(/usr/bin/grep -E '^/.*\.md$' "$d/paste" | /usr/bin/tail -1)
( /bin/sleep 4; test -z "$report" || printf '%s' PANE_REPORT_OK > "$report"; test -z "$report" || seal_report ) >/dev/null 2>&1 &
elif [ -s "$d/paste" ] && [ ! -e "$d/no-report" ]; then
report=$(/usr/bin/grep -E '^/.*\.md$' "$d/paste" | /usr/bin/tail -1)
test -z "$report" || printf '%s' PANE_REPORT_OK > "$report"
test -z "$report" || [ -e "$d/no-seal" ] || seal_report
fi
echo idle > "$d/state"
echo '{"result":{"type":"ok"}}';;
'agent start')
printf '%s\n' "$@" >> "$d/start-attempts"
if [ -s "$d/busy" ]; then
n=$(/bin/cat "$d/busy"); if [ "$n" -gt 0 ]; then
echo $((n - 1)) > "$d/busy"
echo '{"error":{"code":"agent_pane_busy","message":"agent target pane w9:p2 is not an available shell"}}' >&2
exit 1
fi
fi
if [ -e "$d/start-blocked" ]; then
/bin/rm "$d/start-blocked"
echo idle > "$d/state"
echo '{"error":{"code":"agent_not_ready","message":"agent is blocked during startup"}}' >&2
exit 1
fi
if [ -e "$d/start-timeout" ]; then
/bin/rm "$d/start-timeout"
echo idle > "$d/state"
echo '{"error":{"code":"timeout","message":"timed out waiting for agent startup"}}' >&2
exit 1
fi
if [ -e "$d/start-fails" ]; then
echo '{"error":{"code":"agent_kind_unknown","message":"no such agent kind"}}' >&2
exit 1
fi
printf '%s\n' "$@" > "$d/start-args"
echo idle > "$d/state"
echo '{"result":{"agent":{"agent":"claude","agent_status":"idle","pane_id":"w9:p2"}}}';;
'agent prompt')
if [ -e "$d/prompt-refused" ]; then
echo '{"error":{"code":"agent_not_ready","message":"agent w9:p2 is not an active named agent"}}' >&2
exit 1
fi
if printf '%s' "$4" | /usr/bin/grep -q 'BRGR RESULT CHANNEL'; then
/usr/bin/python3 - "$d/native-config.json" <<'PY'
import json,pathlib,sys,subprocess,shlex,os
c=json.loads(pathlib.Path(sys.argv[1]).read_text());settings=json.loads(pathlib.Path(c['argv'][c['argv'].index('--settings')+1]).read_text())
a=c['environment']['BRGR_PARENT_ATTEMPT_ID'];payload={'hook_event_name':'Stop','session_id':a,'cwd':c['workspace'],'last_assistant_message':f'BRGR_REPORT_BEGIN_{a}\nPANE_REPORT_OK\nBRGR_REPORT_END_{a}'}
env=dict(os.environ);env.update(c['environment']);command=settings['hooks']['Stop'][-1]['hooks'][0]['command']
p=subprocess.run(shlex.split(command),input=json.dumps(payload),text=True,env=env,capture_output=True);assert p.returncode==0,p.stderr
PY
echo unknown > "$d/state"
echo '{}'; exit 0
fi
if [ -e "$d/no-report" ]; then echo idle > "$d/state"; echo '{"result":{"type":"ok"}}'; exit 0; fi
report=$(printf '%s\n' "$4" | /usr/bin/grep -E '^/.*\.md$' | /usr/bin/tail -1)
if [ -n "$report" ]; then printf '%s' "$report" > "$d/report-path"; else report=$(/bin/cat "$d/report-path"); fi
if [ -e "$d/hang" ]; then
echo working > "$d/state.hang"
echo '{"result":{"type":"ok"}}'
exit 0
fi
if [ -e "$d/partial-report" ]; then
printf '%s' PARTIAL_REPORT > "$report"
echo unknown > "$d/state"; echo '{}'; exit 0
fi
printf '%s' 'PANE_REPORT_OK' > "$report"
seal_report
if [ -e "$d/unseen" ]; then echo idle > "$d/state"; else echo working > "$d/state"; fi
echo '{"result":{"type":"ok"}}';;
'agent get')
if [ -e "$d/unclassified" ]; then
echo '{"error":{"code":"agent_not_found","message":"native detection is not registered yet"}}' >&2; exit 1
fi
if [ -e "$d/state.hang" ]; then
printf '{"result":{"agent":{"agent":"claude","agent_status":"%s","pane_id":"w9:p2"}}}\n' "$(/bin/cat "$d/state.hang")"
exit 0
fi
s=$(/bin/cat "$d/state")
printf '{"result":{"agent":{"agent":"claude","agent_status":"%s","pane_id":"w9:p2"}}}\n' "$s"
if [ "$s" = working ]; then echo idle > "$d/state"; fi;;
'pane read')
if [ -e "$d/dialog" ] && [ -s "$d/paste" ]; then
printf '%s\n' 'Pick a region' '› 1. us-east' ' 2. eu-west'
exit 0
fi
if [ -e "$d/blockquote" ] && [ -s "$d/paste" ]; then
printf '%s\n' 'Working on it' '> 1. update the config, then skip later steps' ' 2. next'
exit 0
fi
n=$(/bin/cat "$d/menu" 2>/dev/null || echo 0)
if [ "$n" -gt 0 ]; then
echo $((n - 1)) > "$d/menu"
printf '%s\n' ' Update available' '› 1. Update now' ' 2. Skip'
else
if [ -e "$d/start-blocked" ]; then
printf '%s\n' 'Claude Code fixture' 'Accessing workspace:' '' "$(/bin/cat "$d/pane-cwd")" '' '❯ No, exit' 'Yes, I trust this folder'
else
printf '%s\n' 'Claude Code fixture' '› Ask anything'
fi
fi;;
'pane close') echo closed > "$d/closed"; echo '{"result":{"type":"ok"}}';;
*) exit 2;;
esac
"#;
struct PaneFixture {
_temp: TempDir,
home: PathBuf,
workspace: PathBuf,
herdr: PathBuf,
state: PathBuf,
}
fn pane_fixture() -> PaneFixture {
pane_fixture_named("brgr", "work")
}
fn pane_fixture_named(home: &str, workspace: &str) -> PaneFixture {
let temp = TempDir::new().unwrap();
let home = temp.path().join(home);
let workspace = temp.path().join(workspace);
let bin = temp.path().join("bin");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&bin).unwrap();
for (name, body) in [
("claude", CLAUDE_FIXTURE),
("opencode", OPENCODE_FIXTURE),
("herdr", PANE_HERDR_FIXTURE),
] {
fs::write(bin.join(name), body).unwrap();
fs::set_permissions(bin.join(name), fs::Permissions::from_mode(0o700)).unwrap();
}
add_fixture(&home, &bin.join("claude"), &temp.path().join("scratch"));
add_fixture(&home, &bin.join("opencode"), &temp.path().join("scratch"));
PaneFixture {
home,
workspace,
herdr: bin.join("herdr"),
state: bin.join("herdr-state"),
_temp: temp,
}
}
impl PaneFixture {
fn run(&self, extra: &[&str]) -> Value {
self.run_on("local.claude-code", extra)
}
fn run_with_env(&self, extra: &[&str], env: &[(&str, &str)]) -> Value {
self.run_on_with_env("local.claude-code", extra, env)
}
fn run_on(&self, harness: &str, extra: &[&str]) -> Value {
self.run_on_with_env(harness, extra, &[])
}
fn run_on_with_env(&self, harness: &str, extra: &[&str], more: &[(&str, &str)]) -> Value {
let herdr = self.herdr.to_str().unwrap();
let mut args = vec![
"run",
"Explain the build",
"--harness",
harness,
"--workspace",
self.workspace.to_str().unwrap(),
"--foreground",
];
args.extend_from_slice(extra);
json_output(&run(
&self.home,
&args,
&[
&[
("BRGR_OWNER_ID", "codex:pane-test"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", herdr),
][..],
more,
]
.concat(),
))
}
fn state(&self, name: &str) -> String {
fs::read_to_string(self.state.join(name)).unwrap_or_default()
}
}
/// Inside Herdr, a harness with an interactive launch runs as its own TUI in a
/// pane beside the caller, and the report the agent writes is the result.
#[test]
fn pane_mode_runs_the_agent_in_a_pane_and_seals_its_report() {
let fixture = pane_fixture();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
let task = ran["task_id"].as_str().unwrap();
let result = json_output(&run(
&fixture.home,
&["result", task],
&[("BRGR_OWNER_ID", "codex:pane-test")],
));
assert_eq!(result["artifacts"][0]["text"], "PANE_REPORT_OK", "{result}");
// Beside the caller, in the task workspace, and not stealing focus.
let calls = fixture.state("calls.log");
assert!(
calls.contains("pane split --pane w9:p1 --direction right"),
"{calls}"
);
assert!(calls.contains("--no-focus"), "{calls}");
// The same full-permission flags a print-mode run would get.
let start = fixture.state("start-args");
assert!(start.contains("--kind\nclaude"), "{start}");
assert!(
start.contains("--permission-mode\nbypassPermissions"),
"{start}"
);
// The agent can message its owner like any other worker.
let env = fixture.state("pane-env");
assert!(
env.contains(&format!("BRGR_PARENT_TASK_ID={task}")),
"{env}"
);
// Keep the completed TUI until its owner reviews the sealed result.
assert!(fixture.workspace.join(".brgr").exists());
assert!(fixture.state("closed").is_empty());
json_output(&run(
&fixture.home,
&["accept", task],
&[("BRGR_OWNER_ID", "codex:pane-test")],
));
assert!(!fixture.workspace.join(".brgr").exists());
assert_eq!(fixture.state("closed").trim(), "closed");
}
/// A pane that has not drawn its shell prompt yet refuses an agent; the runner
/// waits for it instead of failing the task.
#[test]
fn native_tui_bootstrap_starts_once_even_with_a_slow_shell() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("busy"), "3\n").unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate");
assert_eq!(fixture.state("start-attempts").lines().count(), 1);
}
/// Herdr can recognize a native host's agent on screen yet refuse to prompt it,
/// because it only prompts agents it started and named (seen live with GJC).
/// The prompt is then typed into the pane instead of the run being lost.
#[test]
fn a_prompt_herdr_refuses_for_a_native_host_is_typed_in_instead() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("prompt-refused"), "").unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
let calls = fixture.state("calls.log");
assert!(calls.contains("pane send-text w9:p2"), "{calls}");
}
/// Herdr never classifies GJC or Command Code, so their status is `unknown` for
/// the whole run. A report they wrote but never sealed is sealed once it has
/// stopped changing, instead of the run waiting for its deadline.
#[test]
fn an_unclassified_agent_that_wrote_but_did_not_seal_its_report_is_sealed() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
for flag in ["unclassified", "no-seal"] {
fs::write(fixture.state.join(flag), "").unwrap();
}
let started = Instant::now();
let ran = fixture.run_with_env(&[], &[("BRGR_TEST_REPORT_GRACE_MS", "400")]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
assert!(started.elapsed() < Duration::from_mins(1));
}
/// Herdr does not classify every harness (GJC, Command Code), so a native run
/// can report `unknown` for its whole life. Output that looks like a menu then
/// must be left alone while the agent works.
#[test]
fn menu_like_output_from_an_unclassified_agent_is_not_pressed_mid_run() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
for flag in ["unclassified", "blockquote", "slow-seal"] {
fs::write(fixture.state.join(flag), "").unwrap();
}
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
let calls = fixture.state("calls.log");
assert_eq!(
calls.matches("pane send-keys").count(),
1,
"only the prompt's own Enter may be sent: {calls}"
);
}
/// An agent Herdr never classifies still gets the unknown-screen deadline: a
/// dialog that stands still is reported and fails the run, instead of waiting
/// out the hour-long task deadline. Nothing is pressed on it.
#[test]
fn an_unclassified_agent_stuck_on_an_unknown_dialog_fails_on_the_screen_deadline() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
for flag in ["unclassified", "dialog", "no-report"] {
fs::write(fixture.state.join(flag), "").unwrap();
}
let started = Instant::now();
let ran = fixture.run_with_env(
&[],
&[
("BRGR_TEST_SCREEN_DEADLINE_MS", "500"),
("BRGR_TEST_REPORT_GRACE_MS", "600000"),
],
);
assert_ne!(ran["outcome"], "candidate", "{ran}");
let error = ran["error"].as_str().unwrap_or_default();
assert!(error.contains("no brgr rule answers"), "{error}");
assert!(error.contains("Pick a region"), "{error}");
assert!(started.elapsed() < Duration::from_mins(1));
let calls = fixture.state("calls.log");
assert_eq!(
calls.matches("pane send-keys").count(),
1,
"only the prompt's own Enter may be sent: {calls}"
);
}
/// The default control home on macOS is `~/Library/Application Support/brgr`,
/// so every worktree path has a space. A pane run must survive one in both the
/// control home and the workspace (dogfood bug D4 was a space in that path).
#[test]
fn a_pane_run_works_with_spaces_in_the_home_and_workspace_paths() {
let fixture = pane_fixture_named("Application Support/brgr", "work space");
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
assert!(
fixture.state("pane-cwd").contains("work space"),
"{}",
fixture.state("pane-cwd")
);
}
#[test]
fn owned_native_editor_accepts_input_before_herdr_registers_the_agent() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("unclassified"), "").unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
let calls = fixture.state("calls.log");
assert!(calls.contains("pane send-text w9:p2"), "{calls}");
assert!(!calls.contains("agent prompt"));
assert!(calls.contains("pane send-keys w9:p2 enter"));
// A real native TUI remains alive after its collector exits. Give the
// transport stand-in an independent lifetime for its cleanup check.
let mut native = Command::new("/bin/sleep").arg("15").spawn().unwrap();
let task = ran["task_id"].as_str().unwrap();
let state_path = fixture
.home
.join("runs")
.join(format!("{task}-r1.native-state.json"));
let mut state: Value = serde_json::from_slice(&fs::read(&state_path).unwrap()).unwrap();
state["pid"] = native.id().into();
state["birth"] = String::from_utf8(
Command::new("/bin/ps")
.args(["-p", &native.id().to_string(), "-o", "lstart="])
.output()
.unwrap()
.stdout,
)
.unwrap()
.trim()
.into();
fs::write(&state_path, serde_json::to_vec(&state).unwrap()).unwrap();
json_output(&run(
&fixture.home,
&["accept", ran["task_id"].as_str().unwrap()],
&[("BRGR_OWNER_ID", "codex:pane-test")],
));
assert_eq!(fixture.state("closed").trim(), "closed");
let _ = native.kill();
let _ = native.wait();
}
#[test]
fn native_partial_file_is_not_a_completed_report() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("partial-report"), "").unwrap();
let env = [
("BRGR_OWNER_ID", "codex:pane-test"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", fixture.herdr.to_str().unwrap()),
];
let launched = json_output(&run(
&fixture.home,
&[
"run",
"Partial report probe",
"--harness",
"local.claude-code",
"--workspace",
fixture.workspace.to_str().unwrap(),
],
&env,
));
let task = launched["task_id"].as_str().unwrap();
let deadline = Instant::now() + Duration::from_secs(15);
while !fixture.state("calls.log").contains("agent prompt") {
assert!(Instant::now() < deadline);
thread::sleep(Duration::from_millis(50));
}
thread::sleep(Duration::from_millis(1200));
let result = run(&fixture.home, &["result", task], &env);
assert!(
!result.status.success(),
"partial report was published: {}",
String::from_utf8_lossy(&result.stdout)
);
json_output(&run(&fixture.home, &["cancel", task], &env));
}
#[test]
fn native_conversation_waits_while_busy_then_delivers_once() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("hang"), "").unwrap();
let env = [
("BRGR_OWNER_ID", "codex:pane-test"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", fixture.herdr.to_str().unwrap()),
];
let launched = json_output(&run(
&fixture.home,
&[
"run",
"Explain the build",
"--harness",
"local.claude-code",
"--workspace",
fixture.workspace.to_str().unwrap(),
],
&env,
));
let task = launched["task_id"].as_str().unwrap();
let deadline = Instant::now() + Duration::from_secs(15);
while !fixture.state("calls.log").contains("agent prompt") {
assert!(Instant::now() < deadline);
thread::sleep(Duration::from_millis(50));
}
json_output(&run(
&fixture.home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"note",
"--body",
"NATIVE_FOLLOWUP",
],
&env,
));
thread::sleep(Duration::from_millis(1200));
let calls = fixture.state("calls.log");
assert_eq!(calls.matches("agent prompt w9:p2").count(), 1);
assert!(
!calls.contains("pane send-text"),
"busy TUI received raw input"
);
fs::remove_file(fixture.state.join("hang")).unwrap();
fs::write(fixture.state.join("state.hang"), "idle").unwrap();
let result = json_output(&run(
&fixture.home,
&["wait", task, "--timeout-seconds", "15"],
&env,
));
assert_eq!(result["outcome"], "candidate");
let calls = fixture.state("calls.log");
assert_eq!(calls.matches("agent prompt w9:p2").count(), 2, "{calls}");
assert!(calls.contains("NATIVE_FOLLOWUP"));
json_output(&run(&fixture.home, &["accept", task], &env));
}
/// An agent that starts but stops on a screen Herdr cannot classify — Cline's
/// product notice — times out as "not ready" rather than "blocked". The run
/// asks its owner to look at the pane and waits, instead of failing.
#[test]
fn native_tui_uses_the_registered_executable_and_full_arguments() {
let fixture = pane_fixture();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate");
let task = ran["task_id"].as_str().unwrap();
let config: Value = serde_json::from_slice(
&fs::read(
fixture
.home
.join("runs")
.join(format!("{task}-r1.native-launch.json")),
)
.unwrap(),
)
.unwrap();
assert!(config["executable"].as_str().unwrap().ends_with("/claude"));
assert_eq!(config["argv"][0], "--permission-mode");
assert_eq!(config["argv"][1], "bypassPermissions");
assert!(
!config["argv"]
.as_array()
.unwrap()
.iter()
.any(|arg| arg == "-p")
);
}
/// An approval at startup — a folder trust or MCP prompt — is the owner's to
/// answer in the pane. Once the agent is ready the run withdraws its question,
/// so a finished result is not held back by one nobody needed to reply to.
#[test]
fn native_trust_for_the_owned_workspace_is_resolved_without_a_user_question() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("start-blocked"), "").unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate");
let messages = json_output(&run(
&fixture.home,
&[
"message",
"list",
ran["task_id"].as_str().unwrap(),
"--for",
"owner",
],
&[("BRGR_OWNER_ID", "codex:pane-test")],
));
assert!(messages.as_array().unwrap().is_empty(), "{messages}");
assert!(
fixture
.state("calls.log")
.contains("pane send-keys w9:p2 down enter")
);
}
/// The `opencode` TUI has no effort flag. A task that asks for an effort runs
/// headless, where the effort is honoured, rather than in a pane without it.
#[test]
fn unsupported_tui_effort_requires_an_explicit_headless_request() {
let fixture = pane_fixture();
let output = run(
&fixture.home,
&[
"run",
"Explain",
"--harness",
"local.opencode",
"--effort",
"high",
"--workspace",
fixture.workspace.to_str().unwrap(),
"--foreground",
],
&[
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", fixture.herdr.to_str().unwrap()),
("BRGR_OWNER_ID", "codex:pane-test"),
],
);
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("--headless"));
let headless = fixture.run_on("local.opencode", &["--effort", "high", "--headless"]);
assert_eq!(headless["outcome"], "candidate");
assert!(!fixture.state("calls.log").contains("pane split"));
let paned = fixture.run_on("local.opencode", &[]);
assert_eq!(paned["outcome"], "candidate");
assert!(fixture.state("calls.log").contains("pane split"));
}
#[test]
fn extra_argv_cannot_bypass_a_harness_effort_selector() {
let fixture = pane_fixture();
json_output(&run(
&fixture.home,
&["config", "set", "harness", "local.opencode"],
&[],
));
json_output(&run(
&fixture.home,
&[
"config",
"set",
"argv",
"[\"--variant\",\"high\"]",
"--harness",
"local.opencode",
],
&[],
));
let check = run(&fixture.home, &["config", "check"], &[]);
assert!(!check.status.success());
assert!(String::from_utf8_lossy(&check.stderr).contains("--variant"));
let output = run(
&fixture.home,
&[
"run",
"Explain",
"--harness",
"local.opencode",
"--workspace",
fixture.workspace.to_str().unwrap(),
],
&[
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", fixture.herdr.to_str().unwrap()),
],
);
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("--variant"));
assert!(!fixture.state("calls.log").contains("pane split"));
}
/// `OpenCode` 2 runs in a private server in both modes. It checks its own model
/// names, variant included; its TUI takes none, so a model needs `--headless`.
#[test]
fn opencode_2_runs_standalone_and_takes_a_model_only_headless() {
let fixture = pane_fixture();
let opencode = fixture.herdr.with_file_name("opencode");
fs::write(&opencode, OPENCODE_2_FIXTURE).unwrap();
add_fixture(
&fixture.home,
&opencode,
&fixture.home.with_file_name("scratch-2"),
);
let herdr = [
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", fixture.herdr.to_str().unwrap()),
("BRGR_OWNER_ID", "codex:pane-test"),
];
let model_reached = |task: &str| {
let result = json_output(&run(&fixture.home, &["result", task], &herdr));
assert_eq!(
result["artifacts"][0]["text"].as_str().map(str::trim),
Some("OPENCODE_2_MODEL=opencode/big-pickle#high"),
"{result}"
);
};
let headless = fixture.run_on(
"local.opencode",
&["--headless", "--model", "opencode/big-pickle#high"],
);
assert_eq!(headless["outcome"], "candidate", "{headless}");
let task = headless["task_id"].as_str().unwrap();
model_reached(task);
// A revision of it stays headless rather than asking the TUI for a model.
json_output(&run(
&fixture.home,
&["reject", task, "--reason", "revise"],
&herdr,
));
// Not through `run`, which adds `--headless` to every revise.
let revised = json_output(
&brgr_command()
.arg("--home")
.arg(&fixture.home)
.args(["--json", "revise", task, "Explain again", "--foreground"])
.env("BRGR_SESSION_ID", "fixture-session")
.envs(herdr)
.output()
.unwrap(),
);
assert_eq!(revised["outcome"], "candidate", "{revised}");
assert_eq!(revised["revision"], 2);
model_reached(task);
let modelled = run(
&fixture.home,
&[
"run",
"Explain",
"--harness",
"local.opencode",
"--model",
"opencode/big-pickle",
"--workspace",
fixture.workspace.to_str().unwrap(),
"--foreground",
],
&[
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w9:p1"),
("HERDR_BIN_PATH", fixture.herdr.to_str().unwrap()),
("BRGR_OWNER_ID", "codex:pane-test"),
],
);
assert!(!modelled.status.success());
assert!(String::from_utf8_lossy(&modelled.stderr).contains("--headless"));
assert!(!fixture.state("calls.log").contains("pane split"));
let paned = fixture.run_on("local.opencode", &[]);
assert_eq!(paned["outcome"], "candidate", "{paned}");
let start = fixture.state("start-args");
assert!(start.contains("--kind\nopencode\n--standalone"), "{start}");
}
/// Herdr may never show an agent working: Cursor went from unknown straight to
/// idle once it had finished between two polls. The report is what completes
/// the run, so it must not wait for a working state it will never see.
#[test]
fn pane_mode_completes_on_the_report_without_ever_seeing_work() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("unseen"), "").unwrap();
let started = Instant::now();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
assert!(
started.elapsed() < Duration::from_secs(30),
"{:?}",
started.elapsed()
);
}
/// Cancelling a pane-mode task kills its runner before the runner can close
/// its pane, so the supervisor closes the pane the runner recorded.
#[test]
fn cancelling_a_pane_mode_task_closes_its_pane() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("hang"), "").unwrap();
let mut command = brgr_command();
command
.arg("--home")
.arg(&fixture.home)
.arg("--json")
.args(["run", "Explain the build", "--harness", "local.claude-code"])
.arg("--workspace")
.arg(&fixture.workspace)
.arg("--foreground")
.env_remove("CODEX_THREAD_ID")
.env("BRGR_SESSION_ID", "fixture-session")
.env("BRGR_OWNER_ID", "codex:pane-test")
.env("HERDR_ENV", "1")
.env("HERDR_PANE_ID", "w9:p1")
.env("HERDR_BIN_PATH", &fixture.herdr)
.stdout(Stdio::piped())
.stderr(Stdio::null());
let child = command.spawn().unwrap();
let deadline = Instant::now() + Duration::from_secs(30);
while !fixture.state("calls.log").contains("agent prompt") {
assert!(Instant::now() < deadline, "{}", fixture.state("calls.log"));
thread::sleep(Duration::from_millis(100));
}
let task = fs::read_dir(fixture.home.join("launches"))
.unwrap()
.find_map(|entry| {
let name = entry.unwrap().file_name().into_string().unwrap();
name.strip_suffix(".json").map(str::to_owned)
})
.unwrap();
json_output(&run(
&fixture.home,
&["cancel", &task],
&[("BRGR_OWNER_ID", "codex:pane-test")],
));
let output = child.wait_with_output().unwrap();
let ran: Value = serde_json::from_slice(&output.stdout).unwrap();
// Like every Herdr-backed run, a stopped one is recorded lost rather than
// cancelled: its agent was outside the runner's process group.
assert_eq!(ran["outcome"], "lost", "{ran}");
assert_eq!(
fixture.state("closed").trim(),
"closed",
"{}",
fixture.state("calls.log")
);
let receipts: Vec<_> = fs::read_dir(fixture.home.join("runs"))
.unwrap()
.filter_map(|entry| entry.ok()?.file_name().into_string().ok())
.filter(|name| name.ends_with(".pane.json"))
.collect();
assert_eq!(receipts.len(), 1);
let receipt: Value =
serde_json::from_slice(&fs::read(fixture.home.join("runs").join(&receipts[0])).unwrap())
.unwrap();
assert_eq!(receipt["cleanup"], "closed");
}
/// A Herdr pane id this process does not provably run in — what a Codex tool
/// command inherits from its shared daemon — is never used to split beside a
/// pane. A caller that is not a Codex session has nowhere to be placed, and
/// the error says how to give it a source rather than suggesting no window.
#[test]
fn an_unverified_caller_is_never_split_beside_and_the_error_says_what_to_give() {
let fixture = pane_fixture();
let output = Command::new(brgr())
.arg("--home")
.arg(&fixture.home)
.arg("--json")
.args([
"run",
"Explain",
"--harness",
"local.claude-code",
"--foreground",
"--workspace",
])
.arg(&fixture.workspace)
.env_remove("CODEX_THREAD_ID")
.env_remove("BRGR_TEST_TRUST_HERDR_PANE")
.env("BRGR_SESSION_ID", "fixture-session")
.env("BRGR_OWNER_ID", "codex:pane-test")
.env("HERDR_ENV", "1")
.env("HERDR_PANE_ID", "w9:p1")
.env("HERDR_BIN_PATH", &fixture.herdr)
.env("BRGR_TEST_CALLER_WAIT_MS", "300")
.output()
.unwrap();
// A Codex session's pane is looked up after the call, so the run is
// admitted; what must never happen is a split beside the claimed pane.
assert!(
!fixture
.state("calls.log")
.contains("pane split --pane w9:p1")
);
let _ = output;
}
/// Herdr may call an agent ready while it shows a menu — Codex opened on an
/// update offer. The prompt's Enter would pick the highlighted "Update now", so
/// the run first moves to the offer's skip option, and only then prompts.
#[test]
fn pane_mode_skips_an_update_offer_before_it_prompts() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("menu"), "3\n").unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
let calls = fixture.state("calls.log");
let prompt = calls.find("agent prompt").unwrap();
let skip = calls
.find("pane send-keys w9:p2 down enter")
.unwrap_or_else(|| panic!("the update offer was not skipped: {calls}"));
assert!(skip < prompt, "prompted before skipping the offer: {calls}");
assert!(
!calls[..prompt].contains("pane send-keys w9:p2 enter"),
"Enter alone would have chosen Update now: {calls}"
);
}
/// A run that fails still closes the pane it opened, and says why it failed.
#[test]
fn pane_mode_failure_closes_the_pane_and_names_the_cause() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("start-fails"), "").unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "lost", "{ran}");
let error = ran["error"].as_str().unwrap();
assert!(error.contains("native host bootstrap failed"), "{error}");
assert!(
error.contains("registered native executable could not start"),
"{error}"
);
assert_eq!(fixture.state("closed").trim(), "closed");
}
/// A wide caller gets its worker beside it; a narrow one gets it below, so
/// repeated workers do not squeeze the caller down to nothing.
/// Any brgr command evens out a caller's workers that a manual close or a
/// terminal resize left uneven, and leaves panes brgr did not open alone.
#[test]
fn any_command_evens_out_an_uneven_worker_column() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let runs = home.join("runs");
fs::create_dir_all(&runs).unwrap();
let calls = temp.path().join("calls.log");
let herdr = temp.path().join("herdr");
fs::write(
&herdr,
format!(
r#"#!/bin/sh
echo "$*" >> '{calls}'
case "$1 $2" in
'pane layout') echo '{{"result":{{"layout":{{"panes":[
{{"pane_id":"w9:p1","rect":{{"x":0,"y":0,"width":104,"height":66}}}},
{{"pane_id":"w9:p2","rect":{{"x":104,"y":0,"width":104,"height":50}}}},
{{"pane_id":"w9:p3","rect":{{"x":104,"y":50,"width":104,"height":16}}}},
{{"pane_id":"w9:p9","rect":{{"x":0,"y":66,"width":208,"height":7}}}}],
"splits":[
{{"direction":"right","ratio":0.5,"rect":{{"x":0,"y":0,"width":208,"height":66}}}},
{{"direction":"down","ratio":0.76,"rect":{{"x":104,"y":0,"width":104,"height":66}}}}]}}}}}}';;
*) echo '{{}}';;
esac
"#,
calls = calls.display()
),
)
.unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
for (index, pane) in ["w9:p2", "w9:p3"].into_iter().enumerate() {
fs::write(
runs.join(format!(
"00000000-0000-4000-8000-00000000000{index}-r1.pane.json"
)),
serde_json::to_vec(&json!({
"pane": pane,
"binary": herdr,
"session": null,
"cleanup": "pending",
"caller": "w9:p1",
}))
.unwrap(),
)
.unwrap();
}
json_output(&run(&home, &["status"], &[]));
let log = fs::read_to_string(&calls).unwrap();
assert!(
log.contains("pane resize --pane w9:p3 --direction up --amount 0.2600"),
"{log}"
);
assert!(!log.contains("w9:p9 --direction"), "{log}");
}
#[test]
fn a_narrow_caller_gets_its_worker_below_and_a_wide_one_beside() {
for (width, direction) in [("200", "--direction right"), ("60", "--direction down")] {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("caller-width"), width).unwrap();
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate", "{ran}");
let calls = fixture.state("calls.log");
assert!(calls.contains(direction), "width {width}: {calls}");
}
}
/// An agent that stops without ever writing its report is reminded once and
/// then fails with that cause, instead of the run waiting for its deadline.
#[test]
fn an_agent_that_never_writes_its_report_fails_with_the_cause() {
let fixture = pane_fixture();
fs::create_dir_all(&fixture.state).unwrap();
fs::write(fixture.state.join("no-report"), "").unwrap();
let started = Instant::now();
let ran = fixture.run_with_env(&[], &[("BRGR_TEST_REPORT_GRACE_MS", "300")]);
assert_eq!(ran["outcome"], "lost", "{ran}");
let error = ran["error"].as_str().unwrap();
assert!(
error.contains("finished without writing its report"),
"{error}"
);
assert!(started.elapsed() < Duration::from_mins(1));
let calls = fixture.state("calls.log");
assert_eq!(
calls.matches("agent prompt").count(),
2,
"the task prompt plus exactly one reminder: {calls}"
);
}
/// A read-only request no longer turns the TUI off or into plan mode: the
/// worker runs in its own pane with full permissions.
#[test]
fn a_read_only_request_still_runs_the_tui_with_full_permissions() {
let fixture = pane_fixture();
let ran = fixture.run(&["--permission", "read-only"]);
assert_eq!(ran["outcome"], "candidate");
assert!(fixture.state("calls.log").contains("pane split"));
let args = fixture.state("start-args");
assert!(args.contains("bypassPermissions"), "{args}");
assert!(!args.contains("plan"), "{args}");
json_output(&run(
&fixture.home,
&["config", "set-pane-mode", "false"],
&[],
));
let ran = fixture.run(&[]);
assert_eq!(ran["outcome"], "candidate");
assert_eq!(
fixture
.state("start-args")
.lines()
.filter(|line| *line == "bypassPermissions")
.count(),
1
);
}
const RECURSIVE_GJC_FIXTURE: &str = r#"#!/bin/sh
set -eu
case "${1:-}" in
--version) echo 'gjc v-recursive-fixture'; exit 0;;
--help)
printf '%s\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'
exit 0;;
esac
prompt_file=
for argument in "$@"; do
case "$argument" in @*) prompt_file=${argument#@};; esac
done
test -f "$prompt_file"
if test -n "${BRGR_PARENT_ATTEMPT_ID:-}"; then
if /usr/bin/grep -q UNSETTLED "$prompt_file"; then
"$BRGR_BIN" --json --headless run LEAF --harness local.gjc --workspace "$PWD" --foreground >/dev/null
elif /usr/bin/grep -q ROOT "$prompt_file"; then
child_json=$("$BRGR_BIN" --json --headless run CHILD --harness local.gjc --workspace "$PWD")
child_task=$(printf '%s\n' "$child_json" | /usr/bin/sed -n 's/.*"task_id":"\([^"]*\)".*/\1/p')
test -n "$child_task"
"$BRGR_BIN" --json wait "$child_task" --timeout-seconds 10 >/dev/null
"$BRGR_BIN" --json result "$child_task" >/dev/null
"$BRGR_BIN" --json accept "$child_task" --reason 'child artifact checked' >/dev/null
elif /usr/bin/grep -q CHILD "$prompt_file"; then
child_json=$("$BRGR_BIN" --json --headless run LEAF --harness local.gjc --workspace "$PWD")
child_task=$(printf '%s\n' "$child_json" | /usr/bin/sed -n 's/.*"task_id":"\([^"]*\)".*/\1/p')
test -n "$child_task"
"$BRGR_BIN" --json wait "$child_task" --timeout-seconds 10 >/dev/null
"$BRGR_BIN" --json result "$child_task" >/dev/null
"$BRGR_BIN" --json accept "$child_task" --reason 'leaf artifact checked' >/dev/null
fi
fi
printf '%s\n' '{"type":"message_end","message":{"role":"assistant","content":[{"type":"text","text":"BRGR_RECURSIVE_OK"}]}}'
printf '%s\n' '{"type":"agent_end","stopReason":"completed"}'
"#;
#[test]
fn a_worker_can_delegate_twice_and_decide_each_child_before_reporting() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(&executable, RECURSIVE_GJC_FIXTURE).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let root = json_output(&run(
&home,
&[
"run",
"ROOT",
"--harness",
"local.gjc",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:recursive-root")],
));
assert_eq!(root["outcome"], "candidate");
let root_task = root["task_id"].as_str().unwrap();
let waited = json_output(&run(
&home,
&["wait", root_task, "--timeout-seconds", "2"],
&[("BRGR_OWNER_ID", "codex:recursive-root")],
));
assert_eq!(waited["outcome"], "candidate");
let store = brgr_store::Store::open(home.join("store")).unwrap();
let root_id = root_task.parse().unwrap();
let tasks = store.tasks(10).unwrap();
assert_eq!(tasks.len(), 3);
let child = tasks.iter().find(|task| task.objective == "CHILD").unwrap();
let leaf = tasks.iter().find(|task| task.objective == "LEAF").unwrap();
let (child_parent, child_attempt, child_depth) =
store.delegation_parent(child.task_id).unwrap().unwrap();
assert_eq!(child_parent, root_id);
assert_eq!(child_depth, 1);
let (leaf_parent, _, leaf_depth) = store.delegation_parent(leaf.task_id).unwrap().unwrap();
assert_eq!(leaf_parent, child.task_id);
assert_eq!(leaf_depth, 2);
assert_eq!(child.owner_id.as_str(), format!("worker:{child_attempt}"));
assert_eq!(store.unsettled_children(child_attempt).unwrap(), 0);
let root_result = json_output(&run(
&home,
&["result", root_task],
&[("BRGR_OWNER_ID", "codex:recursive-root")],
));
assert_eq!(root_result["artifacts"][0]["text"], "BRGR_RECURSIVE_OK");
let unsettled = json_output(&run(
&home,
&[
"run",
"UNSETTLED",
"--harness",
"local.gjc",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:recursive-root")],
));
assert_eq!(unsettled["outcome"], "failed");
assert!(
unsettled["error"]
.as_str()
.unwrap()
.contains("child task(s) remain")
);
}
#[test]
fn recursive_worker_uses_sibling_worktrees_from_a_git_parent() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
let executable = temp.path().join("gjc");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
fs::write(&executable, RECURSIVE_GJC_FIXTURE).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let root = json_output(&run(
&home,
&[
"run",
"ROOT",
"--harness",
"local.gjc",
"--workspace",
repository.to_str().unwrap(),
"--enable-delegation",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:git-recursive")],
));
assert_eq!(root["outcome"], "candidate");
let store = brgr_store::Store::open(home.join("store")).unwrap();
let tasks = store.tasks(10).unwrap();
assert_eq!(tasks.len(), 3);
let parent = home.canonicalize().unwrap().join("worktrees/repo");
assert!(
tasks
.iter()
.all(|task| Path::new(&task.workspace).starts_with(&parent)),
"workspaces: {:?}",
tasks.iter().map(|task| &task.workspace).collect::<Vec<_>>()
);
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"seed\n"
);
}
#[test]
fn rejected_delegated_child_revises_inside_parent_git_worktree() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let root_owner = [("BRGR_OWNER_ID", "codex:git-revise")];
let root = json_output(&run(
&home,
&[
"run",
"SLOW",
"--workspace",
repository.to_str().unwrap(),
"--enable-delegation",
"--deadline-seconds",
"20",
],
&root_owner,
));
let root_task = root["task_id"].as_str().unwrap();
let root_id = root_task.parse().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
let root_attempt = (0..200)
.find_map(|_| {
let found = store.active_message_attempt(root_id).ok();
if found.is_none() {
thread::sleep(Duration::from_millis(25));
}
found
})
.expect("root attempt did not become active");
let parent_workspace = store.task(root_id).unwrap().workspace;
assert!(Path::new(&parent_workspace).starts_with(home.canonicalize().unwrap()));
let worker_owner = format!("worker:{root_attempt}");
let root_attempt_text = root_attempt.to_string();
let worker = [
("BRGR_OWNER_ID", worker_owner.as_str()),
("BRGR_SESSION_ID", worker_owner.as_str()),
("BRGR_PARENT_TASK_ID", root_task),
("BRGR_PARENT_ATTEMPT_ID", root_attempt_text.as_str()),
];
let child = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
&parent_workspace,
"--foreground",
],
&worker,
));
assert_eq!(child["outcome"], "candidate");
let child_task = child["task_id"].as_str().unwrap();
assert_eq!(
json_output(&run(
&home,
&["reject", child_task, "--reason", "revise fixture"],
&worker
))["verdict"],
"rejected"
);
let revised = json_output(&run(
&home,
&[
"revise",
child_task,
"BRGR_FIXTURE_OK",
"--workspace",
&parent_workspace,
"--foreground",
],
&worker,
));
assert_eq!(revised["outcome"], "candidate");
assert_eq!(revised["revision"], 2);
let child_id = child_task.parse().unwrap();
assert_eq!(
store.delegation_parent(child_id).unwrap(),
Some((root_id, root_attempt, 1))
);
assert_eq!(store.unsettled_children(root_attempt).unwrap(), 1);
assert_eq!(
json_output(&run(&home, &["accept", child_task], &worker))["verdict"],
"accepted"
);
assert_eq!(store.unsettled_children(root_attempt).unwrap(), 0);
json_output(&run(&home, &["cancel", root_task], &root_owner));
let terminal = json_output(&run(
&home,
&["wait", root_task, "--timeout-seconds", "10"],
&root_owner,
));
assert_eq!(terminal["outcome"], "cancelled");
}
#[test]
fn tree_status_shows_question_and_tree_cancel_stops_both_processes() {
let (_temp, home, workspace) = gjc_home();
let root_owner = [("BRGR_OWNER_ID", "codex:tree-cancel")];
let root = json_output(&run(
&home,
&[
"run",
"SLOW",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
],
&root_owner,
));
let root_task = root["task_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
let root_attempt = (0..100)
.find_map(|_| {
let found = store
.active_message_attempt(root_task.parse().unwrap())
.ok();
if found.is_none() {
thread::sleep(Duration::from_millis(20));
}
found
})
.expect("root attempt did not become active");
let worker_owner = format!("worker:{root_attempt}");
let root_attempt_text = root_attempt.to_string();
let worker = [
("BRGR_OWNER_ID", worker_owner.as_str()),
("BRGR_SESSION_ID", worker_owner.as_str()),
("BRGR_PARENT_TASK_ID", root_task),
("BRGR_PARENT_ATTEMPT_ID", root_attempt_text.as_str()),
];
let child = json_output(&run(
&home,
&["run", "SLOW", "--workspace", workspace.to_str().unwrap()],
&worker,
));
let child_task = child["task_id"].as_str().unwrap();
for _ in 0..100 {
if store
.active_message_attempt(child_task.parse().unwrap())
.is_ok()
{
break;
}
thread::sleep(Duration::from_millis(20));
}
json_output(&run(
&home,
&[
"message",
"send",
child_task,
"--to",
"worker",
"--kind",
"question",
"--body",
"Need direction",
],
&worker,
));
let tree = json_output(&run(&home, &["status", root_task, "--tree"], &root_owner));
assert_eq!(tree["nodes"].as_array().unwrap().len(), 2);
let child_row = tree["nodes"]
.as_array()
.unwrap()
.iter()
.find(|row| row["task_id"] == child_task)
.unwrap();
assert_eq!(child_row["waiting_for"], "question");
assert!(child_row["remaining_seconds"].as_i64().is_some());
let cancelled = json_output(&run(&home, &["cancel", root_task, "--tree"], &root_owner));
assert_eq!(cancelled["requested"].as_array().unwrap().len(), 2);
let child_terminal = json_output(&run(
&home,
&["wait", child_task, "--timeout-seconds", "10"],
&worker,
));
assert_eq!(child_terminal["outcome"], "cancelled", "{child_terminal}");
assert_eq!(
json_output(&run(
&home,
&["wait", root_task, "--timeout-seconds", "10"],
&root_owner
))["outcome"],
"cancelled"
);
}
#[test]
fn committed_cancellation_intent_stops_running_worker_without_cancel_file() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:cancel-intent")];
let task = json_output(&run(
&home,
&["run", "SLOW", "--workspace", workspace.to_str().unwrap()],
&owner,
))["task_id"]
.as_str()
.unwrap()
.to_owned();
let store = brgr_store::Store::open(home.join("store")).unwrap();
let task_id = task.parse().unwrap();
for _ in 0..100 {
if store.active_message_attempt(task_id).is_ok() {
break;
}
thread::sleep(Duration::from_millis(20));
}
assert!(store.active_message_attempt(task_id).is_ok());
store.record_cancellation_intents(task_id, false).unwrap();
let result = json_output(&run(
&home,
&["wait", &task, "--timeout-seconds", "10"],
&owner,
));
assert_eq!(result["outcome"], "cancelled");
}
#[test]
fn terminal_tree_cancellation_does_not_cancel_a_later_revision() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:cancel-revision")];
let first = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(first["outcome"], "candidate");
let task = first["task_id"].as_str().unwrap();
json_output(&run(&home, &["cancel", task, "--tree"], &owner));
json_output(&run(&home, &["reject", task, "--reason", "revise"], &owner));
let revised = json_output(&run(
&home,
&["revise", task, "BRGR_FIXTURE_OK", "--foreground"],
&owner,
));
assert_eq!(revised["outcome"], "candidate");
assert_eq!(revised["revision"], 2);
}
#[test]
fn relative_control_home_is_canonical_before_worker_delegation() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("relative-home");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(&executable, RECURSIVE_GJC_FIXTURE).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let output = brgr_command()
.current_dir(temp.path())
.args([
"--home",
"relative-home",
"--json",
"--headless",
"run",
"ROOT",
"--harness",
"local.gjc",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
"--foreground",
])
.env("BRGR_SESSION_ID", "fixture-session")
.env("BRGR_OWNER_ID", "codex:relative-home")
.env_remove("CODEX_THREAD_ID")
.output()
.unwrap();
let result = json_output(&output);
assert_eq!(result["outcome"], "candidate");
assert_eq!(
brgr_store::Store::open(home.join("store"))
.unwrap()
.tasks(10)
.unwrap()
.len(),
3
);
}
struct MessageFixture {
_temp: TempDir,
home: PathBuf,
task: String,
attempt: String,
}
fn start_message_fixture() -> MessageFixture {
let (temp, home, workspace) = gjc_home();
let launch = json_output(&run(
&home,
&[
"run",
"SLOW",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
],
&[("BRGR_OWNER_ID", "codex:message-test")],
));
let task = launch["task_id"].as_str().unwrap();
let task_id = task.parse().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
let attempt = (0..100)
.find_map(|_| {
let found = store.active_message_attempt(task_id).ok();
if found.is_none() {
thread::sleep(Duration::from_millis(20));
}
found
})
.expect("worker attempt did not become active");
MessageFixture {
_temp: temp,
home,
task: task.to_owned(),
attempt: attempt.to_string(),
}
}
fn worker_question_roundtrip(
home: &Path,
task: &str,
owner: &[(&str, &str)],
worker: &[(&str, &str)],
) {
let question = json_output(&run(
home,
&[
"message",
"send",
task,
"--to",
"owner",
"--kind",
"question",
"--body",
"Which token?",
],
worker,
));
let question_id = question["message_id"].as_str().unwrap();
let received = json_output(&run(
home,
&[
"message",
"wait",
task,
"--for",
"owner",
"--timeout-seconds",
"2",
],
owner,
));
assert_eq!(received["message_id"], question_id);
assert_eq!(received["body"], "Which token?");
json_output(&run(
home,
&["message", "ack", task, question_id, "--for", "owner"],
owner,
));
let answer = json_output(&run(
home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"reply",
"--reply-to",
question_id,
"--body",
"TOKEN_OK",
],
owner,
));
let answer_id = answer["message_id"].as_str().unwrap();
let worker_received = json_output(&run(
home,
&[
"message",
"wait",
task,
"--for",
"worker",
"--timeout-seconds",
"2",
],
worker,
));
assert_eq!(worker_received["message_id"], answer_id);
assert_eq!(worker_received["in_reply_to"], question_id);
json_output(&run(
home,
&["message", "ack", task, answer_id, "--for", "worker"],
worker,
));
}
fn assert_message_replay_and_direction(
home: &Path,
task: &str,
owner: &[(&str, &str)],
question_id: &str,
) {
assert_eq!(
json_output(&run(
home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"question",
"--body",
"Confirm receipt?",
"--request-id",
question_id,
],
owner,
))["message_id"],
question_id
);
assert!(
!run(
home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"question",
"--body",
"Changed replay",
"--request-id",
question_id,
],
owner,
)
.status
.success()
);
assert!(
!run(
home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"reply",
"--body",
"Invalid self reply",
"--reply-to",
question_id,
],
owner,
)
.status
.success()
);
}
fn unanswered_message_count(home: &Path, task: &str, attempt: &str) -> u64 {
brgr_store::Store::open(home.join("store"))
.unwrap()
.unsettled_questions(task.parse().unwrap(), attempt.parse().unwrap())
.unwrap()
}
fn terminal_worker_can_read_and_ack_existing_message(
home: &Path,
task: &str,
owner: &[(&str, &str)],
worker: &[(&str, &str)],
) {
let pending = json_output(&run(
home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"note",
"--body",
"Read after exit",
],
owner,
));
let message_id = pending["message_id"].as_str().unwrap();
json_output(&run(home, &["cancel", task], owner));
let settled = json_output(&run(
home,
&["wait", task, "--timeout-seconds", "10"],
owner,
));
assert_eq!(settled["outcome"], "cancelled");
let unread = json_output(&run(
home,
&["message", "list", task, "--for", "worker"],
worker,
));
assert_eq!(unread[0]["message_id"], message_id);
json_output(&run(
home,
&["message", "ack", task, message_id, "--for", "worker"],
worker,
));
let remaining = json_output(&run(
home,
&["message", "list", task, "--for", "worker"],
worker,
));
assert_eq!(remaining.as_array().unwrap().len(), 0);
let other_attempt = brgr_protocol::AttemptId::new().to_string();
let mut wrong_worker = worker.to_vec();
wrong_worker.push(("BRGR_PARENT_ATTEMPT_ID", other_attempt.as_str()));
assert!(
!run(
home,
&["message", "list", task, "--for", "worker"],
&wrong_worker,
)
.status
.success()
);
assert!(
!run(
home,
&[
"message",
"send",
task,
"--to",
"owner",
"--kind",
"note",
"--body",
"Stale attempt"
],
worker,
)
.status
.success()
);
}
#[test]
fn owner_message_wait_survives_the_gap_before_attempt_creation() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let scratch = temp.path().join("scratch");
fs::create_dir_all(&workspace).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &scratch);
let launch = json_output(&run(
&home,
&["run", "SLOW", "--workspace", workspace.to_str().unwrap()],
&[
("BRGR_OWNER_ID", "codex:message-gap"),
("BRGR_TEST_EXIT_BEFORE_TASK_CLAIM", "1"),
],
));
let task = launch["task_id"].as_str().unwrap();
let started = Instant::now();
let wait = run(
&home,
&[
"message",
"wait",
task,
"--for",
"owner",
"--timeout-seconds",
"1",
],
&[("BRGR_OWNER_ID", "codex:message-gap")],
);
assert!(!wait.status.success());
assert!(started.elapsed() >= Duration::from_millis(900));
assert!(String::from_utf8_lossy(&wait.stderr).contains("before the wait timeout"));
}
#[test]
fn worker_receives_criteria_scope_and_role_before_reporting() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-instructions-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nfor item in \"$@\"; do case \"$item\" in @*) prompt=${item#@};; esac; done\nif /usr/bin/grep -q CHECK_INSTRUCTIONS \"$prompt\"; then\n /usr/bin/grep -q 'snapshot matches' \"$prompt\" || exit 7\n /usr/bin/grep -q 'Only docs' \"$prompt\" || exit 8\n /usr/bin/grep -q 'Act as reviewer' \"$prompt\" || exit 9\nfi\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"INSTRUCTIONS_OK\"}]}}'\nprintf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let result = json_output(&run(
&home,
&[
"run",
"CHECK_INSTRUCTIONS",
"--workspace",
workspace.to_str().unwrap(),
"--criterion",
"snapshot matches",
"--scope",
"Only docs",
"--role-instruction",
"Act as reviewer",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:instructions")],
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let detail = json_output(&run(
&home,
&["result", task],
&[("BRGR_OWNER_ID", "codex:instructions")],
));
assert_eq!(detail["artifacts"][0]["text"], "INSTRUCTIONS_OK");
let stored = brgr_store::Store::open(home.join("store"))
.unwrap()
.task(task.parse().unwrap())
.unwrap();
assert_eq!(stored.instructions.scope, ["Only docs"]);
assert_eq!(stored.instructions.role, ["Act as reviewer"]);
}
#[test]
fn idle_codex_parent_receives_completion_without_another_user_turn() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let scratch = temp.path().join("scratch");
let herdr = temp.path().join("herdr");
let agent_state = temp.path().join("agent-state");
let prompts = temp.path().join("prompts");
fs::create_dir_all(&workspace).unwrap();
fs::write(&agent_state, "working").unwrap();
fs::write(
&herdr,
"#!/bin/sh\nif [ \"$1\" = --session ]; then shift 2; fi\ncase \"$1 $2\" in\n 'agent get') state=$(/bin/cat \"$BRGR_TEST_AGENT_STATE\"); printf '{\"result\":{\"agent\":{\"agent\":\"codex\",\"pane_id\":\"w1:p1\",\"agent_status\":\"%s\",\"agent_session\":{\"value\":\"session-a\"}}}}\\n' \"$state\";;\n 'agent prompt') printf '%s\\n' \"$4\" >> \"$BRGR_TEST_PROMPTS\"; printf '{}\\n';;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &scratch);
let envs = [
("CODEX_THREAD_ID", "session-a"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-a"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_WORKSPACE_ID", "w1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("HERDR_SESSION", "fixture-herdr"),
("BRGR_TEST_AGENT_STATE", agent_state.to_str().unwrap()),
("BRGR_TEST_PROMPTS", prompts.to_str().unwrap()),
];
let launch = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
],
&envs,
));
let task = launch["task_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
for _ in 0..100 {
if store.latest_result(task.parse().unwrap()).is_ok() {
break;
}
thread::sleep(Duration::from_millis(50));
}
assert!(store.latest_result(task.parse().unwrap()).is_ok());
thread::sleep(Duration::from_millis(700));
assert!(!prompts.exists(), "busy Codex parent was prompted");
assert_duplicate_notification_dispatcher_exits(&home, task, &envs);
fs::write(&agent_state, "idle").unwrap();
for _ in 0..100 {
if prompts.exists() {
break;
}
thread::sleep(Duration::from_millis(50));
}
let prompt = fs::read_to_string(&prompts).expect("completion was not delivered");
assert!(prompt.contains("FROM BRGR"));
assert!(prompt.contains(task));
for _ in 0..100 {
if store
.pending_notifications_for_task(task.parse().unwrap())
.unwrap()
.is_empty()
{
break;
}
thread::sleep(Duration::from_millis(50));
}
assert!(
store
.pending_notifications_for_task(task.parse().unwrap())
.unwrap()
.is_empty()
);
thread::sleep(Duration::from_millis(700));
assert_eq!(fs::read_to_string(prompts).unwrap(), prompt);
}
#[test]
fn idle_codex_parent_is_told_a_run_failed_with_its_reason() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let scratch = temp.path().join("scratch");
let herdr = temp.path().join("herdr");
let agent_state = temp.path().join("agent-state");
let prompts = temp.path().join("prompts");
fs::create_dir_all(&workspace).unwrap();
fs::write(&agent_state, "working").unwrap();
fs::write(
&herdr,
"#!/bin/sh\nif [ \"$1\" = --session ]; then shift 2; fi\ncase \"$1 $2\" in\n 'agent get') state=$(/bin/cat \"$BRGR_TEST_AGENT_STATE\"); printf '{\"result\":{\"agent\":{\"agent\":\"codex\",\"pane_id\":\"w1:p1\",\"agent_status\":\"%s\",\"agent_session\":{\"value\":\"session-a\"}}}}\\n' \"$state\";;\n 'agent prompt') printf '%s\\n' \"$4\" >> \"$BRGR_TEST_PROMPTS\"; printf '{}\\n';;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let executable = temp.path().join("gjc");
fs::write(&executable, "#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-failed-notice-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nfor item in \"$@\"; do case \"$item\" in @*) prompt=${item#@};; esac; done\nif /usr/bin/grep -q BRGR_FIXTURE_OK \"$prompt\"; then\n printf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"BRGR_FIXTURE_OK\"}]}}'\n printf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\nelse\n exit 7\nfi\n").unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &scratch);
let envs = [
("CODEX_THREAD_ID", "session-a"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-a"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_WORKSPACE_ID", "w1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("HERDR_SESSION", "fixture-herdr"),
("BRGR_TEST_AGENT_STATE", agent_state.to_str().unwrap()),
("BRGR_TEST_PROMPTS", prompts.to_str().unwrap()),
];
let launch = json_output(&run(
&home,
&["run", "FAIL", "--workspace", workspace.to_str().unwrap()],
&envs,
));
let task = launch["task_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
for _ in 0..100 {
if store.latest_result(task.parse().unwrap()).is_ok() {
break;
}
thread::sleep(Duration::from_millis(50));
}
assert!(store.latest_result(task.parse().unwrap()).is_ok());
thread::sleep(Duration::from_millis(700));
assert!(!prompts.exists(), "busy Codex parent was prompted");
assert_duplicate_notification_dispatcher_exits(&home, task, &envs);
fs::write(&agent_state, "idle").unwrap();
for _ in 0..100 {
if prompts.exists() {
break;
}
thread::sleep(Duration::from_millis(50));
}
let prompt = fs::read_to_string(&prompts).expect("completion was not delivered");
assert!(prompt.contains("FROM BRGR"), "{prompt}");
assert!(prompt.contains("\"type\":\"brgr_failure\""), "{prompt}");
assert!(prompt.contains("\"outcome\":\"failed\""), "{prompt}");
assert!(prompt.contains(task), "{prompt}");
assert!(prompt.contains("brgr result"), "{prompt}");
}
/// A worker's question reaches an idle Codex owner without another user turn.
///
/// Only completions used to be pushed, so a question waited until the worker's
/// own `message wait` timed out unless the owner happened to poll. The notice is
/// held while Codex is busy, sent once per session, and stops once answered.
#[test]
fn idle_codex_owner_is_told_when_a_worker_asks() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
let agent_state = temp.path().join("agent-state");
let prompts = temp.path().join("prompts");
fs::create_dir_all(&workspace).unwrap();
fs::write(&agent_state, "working").unwrap();
fs::write(
&herdr,
"#!/bin/sh\nif [ \"$1\" = --session ]; then shift 2; fi\ncase \"$1 $2\" in\n 'agent get') state=$(/bin/cat \"$BRGR_TEST_AGENT_STATE\"); printf '{\"result\":{\"agent\":{\"agent\":\"codex\",\"pane_id\":\"w1:p1\",\"agent_status\":\"%s\",\"agent_session\":{\"value\":\"session-q\"}}}}\\n' \"$state\";;\n 'agent prompt') printf '%s\\n' \"$4\" >> \"$BRGR_TEST_PROMPTS\"; printf '{}\\n';;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &gjc_fixture(), &temp.path().join("scratch"));
let owner = [
("CODEX_THREAD_ID", "session-q"),
("BRGR_OWNER_ID", "codex:session-q"),
("BRGR_SESSION_ID", "session-q"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_WORKSPACE_ID", "w1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("HERDR_SESSION", "fixture-herdr"),
("BRGR_TEST_AGENT_STATE", agent_state.to_str().unwrap()),
("BRGR_TEST_PROMPTS", prompts.to_str().unwrap()),
];
let launch = json_output(&run(
&home,
&[
"run",
"SLOW",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
],
&owner,
));
let task = launch["task_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
let attempt = (0..100)
.find_map(|_| {
let found = store.active_message_attempt(task.parse().unwrap()).ok();
if found.is_none() {
thread::sleep(Duration::from_millis(20));
}
found
})
.expect("worker attempt did not become active")
.to_string();
let worker_owner = format!("worker:{attempt}");
let worker = [
("BRGR_OWNER_ID", worker_owner.as_str()),
("BRGR_SESSION_ID", worker_owner.as_str()),
("BRGR_PARENT_TASK_ID", task),
("BRGR_PARENT_ATTEMPT_ID", attempt.as_str()),
];
let question = json_output(&run(
&home,
&[
"message",
"send",
task,
"--to",
"owner",
"--kind",
"question",
"--body",
"Which token?",
],
&worker,
));
let question_id = question["message_id"].as_str().unwrap().to_owned();
thread::sleep(Duration::from_millis(1_200));
assert!(!prompts.exists(), "a busy Codex owner was prompted");
fs::write(&agent_state, "idle").unwrap();
let delivered = (0..100)
.find_map(|_| {
let text = fs::read_to_string(&prompts).unwrap_or_default();
if text.contains(&question_id) {
return Some(text);
}
thread::sleep(Duration::from_millis(50));
None
})
.expect("the question was not delivered");
assert!(delivered.contains("FROM BRGR"), "{delivered}");
assert!(delivered.contains("brgr_question"), "{delivered}");
assert!(delivered.contains(task), "{delivered}");
// Several more polls while the question is still open: told once, not again.
thread::sleep(Duration::from_millis(1_500));
let text = fs::read_to_string(&prompts).unwrap();
// The id also appears inside the notice's own instructions, so count
// notices by their `message_id` field.
let field = format!("\"message_id\":\"{question_id}\"");
assert_eq!(text.matches(field.as_str()).count(), 1, "{text}");
json_output(&run(&home, &["cancel", task], &owner));
}
/// A worker's question reaches an idle Claude Code owner too. Herdr keeps no
/// session id for Claude Code, so its pane and agent kind are the identity, and
/// the owner id comes from `CLAUDE_CODE_SESSION_ID`.
///
/// Only completions used to be pushed, so a question waited until the worker's
/// own `message wait` timed out unless the owner happened to poll. The notice is
/// held while Codex is busy, sent once per session, and stops once answered.
#[test]
fn idle_claude_owner_is_told_when_a_worker_asks() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
let agent_state = temp.path().join("agent-state");
let prompts = temp.path().join("prompts");
fs::create_dir_all(&workspace).unwrap();
fs::write(&agent_state, "working").unwrap();
fs::write(
&herdr,
"#!/bin/sh\nif [ \"$1\" = --session ]; then shift 2; fi\ncase \"$1 $2\" in\n 'agent get') state=$(/bin/cat \"$BRGR_TEST_AGENT_STATE\"); printf '{\"result\":{\"agent\":{\"agent\":\"claude\",\"pane_id\":\"w1:p1\",\"agent_status\":\"%s\"}}}\\n' \"$state\";;\n 'agent prompt') printf '%s\\n' \"$4\" >> \"$BRGR_TEST_PROMPTS\"; printf '{}\\n';;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &gjc_fixture(), &temp.path().join("scratch"));
let owner = [
("CLAUDE_CODE_SESSION_ID", "session-q"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_WORKSPACE_ID", "w1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("HERDR_SESSION", "fixture-herdr"),
("BRGR_TEST_AGENT_STATE", agent_state.to_str().unwrap()),
("BRGR_TEST_PROMPTS", prompts.to_str().unwrap()),
];
let launch = json_output(&run(
&home,
&[
"run",
"SLOW",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
],
&owner,
));
let task = launch["task_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
let attempt = (0..100)
.find_map(|_| {
let found = store.active_message_attempt(task.parse().unwrap()).ok();
if found.is_none() {
thread::sleep(Duration::from_millis(20));
}
found
})
.expect("worker attempt did not become active")
.to_string();
let worker_owner = format!("worker:{attempt}");
let worker = [
("BRGR_OWNER_ID", worker_owner.as_str()),
("BRGR_SESSION_ID", worker_owner.as_str()),
("BRGR_PARENT_TASK_ID", task),
("BRGR_PARENT_ATTEMPT_ID", attempt.as_str()),
];
let question = json_output(&run(
&home,
&[
"message",
"send",
task,
"--to",
"owner",
"--kind",
"question",
"--body",
"Which token?",
],
&worker,
));
let question_id = question["message_id"].as_str().unwrap().to_owned();
thread::sleep(Duration::from_millis(1_200));
assert!(!prompts.exists(), "a busy Claude owner was prompted");
fs::write(&agent_state, "idle").unwrap();
let delivered = (0..100)
.find_map(|_| {
let text = fs::read_to_string(&prompts).unwrap_or_default();
if text.contains(&question_id) {
return Some(text);
}
thread::sleep(Duration::from_millis(50));
None
})
.expect("the question was not delivered");
assert!(delivered.contains("FROM BRGR"), "{delivered}");
assert!(delivered.contains("brgr_question"), "{delivered}");
assert!(delivered.contains(task), "{delivered}");
// Several more polls while the question is still open: told once, not again.
thread::sleep(Duration::from_millis(1_500));
let text = fs::read_to_string(&prompts).unwrap();
// The id also appears inside the notice's own instructions, so count
// notices by their `message_id` field.
let field = format!("\"message_id\":\"{question_id}\"");
assert_eq!(text.matches(field.as_str()).count(), 1, "{text}");
json_output(&run(&home, &["cancel", task], &owner));
}
/// A notification dispatcher stops once its control home is gone.
///
/// It polls for up to a day, and an open database connection keeps reading a
/// deleted database, so every test that deleted its home left one running.
#[test]
fn a_dispatcher_exits_when_its_control_home_is_removed() {
let (temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:dispatcher-exit")];
let launched = json_output(&run(
&home,
&["run", "SLOW", "--workspace", workspace.to_str().unwrap()],
&owner,
));
let task = launched["task_id"].as_str().unwrap();
let mut dispatcher = brgr_command()
.arg("--home")
.arg(&home)
.args(["__notify", task])
.env_remove("HERDR_ENV")
.spawn()
.unwrap();
// Still polling: the run is not finished.
thread::sleep(Duration::from_millis(800));
assert!(
dispatcher.try_wait().unwrap().is_none(),
"exited before the home was removed"
);
// Not cancelled first: a final result also ends the dispatcher, and this
// must show that a vanished home alone is enough.
fs::remove_dir_all(&home).unwrap();
let exited = (0..50).any(|_| {
if dispatcher.try_wait().unwrap().is_some() {
return true;
}
thread::sleep(Duration::from_millis(100));
false
});
if !exited {
let _ = dispatcher.kill();
}
// The run's supervisor names the removed home in its argv; stop it too.
let _ = Command::new("/usr/bin/pkill")
.args(["-f", home.to_str().unwrap()])
.status();
drop(temp);
assert!(exited, "the dispatcher kept polling a removed control home");
}
#[test]
fn missing_herdr_codex_session_is_reported_from_exact_bound_pane() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
let reported = temp.path().join("reported-session");
fs::create_dir_all(&workspace).unwrap();
fs::write(&herdr, r#"#!/bin/sh
case "$1 $2" in
'agent get')
if [ -s "$BRGR_TEST_SESSION_FILE" ]; then
printf '{"result":{"agent":{"agent":"codex","pane_id":"w1:p1","agent_status":"working","agent_session":{"value":"%s"}}}}\n' "$(/bin/cat "$BRGR_TEST_SESSION_FILE")"
else
printf '%s\n' '{"result":{"agent":{"agent":"codex","pane_id":"w1:p1","agent_status":"working"}}}'
fi;;
'pane report-agent-session')
test "$3" = w1:p1 || exit 7
while [ "$#" -gt 0 ]; do
if [ "$1" = --source ]; then test "$2" = herdr:codex || exit 8; fi
if [ "$1" = --agent-session-id ]; then printf '%s' "$2" > "$BRGR_TEST_SESSION_FILE"; fi
shift
done
printf '%s\n' '{}';;
*) exit 9;;
esac
"#).unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let envs = [
("CODEX_THREAD_ID", "session-a"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-a"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("BRGR_TEST_SESSION_FILE", reported.to_str().unwrap()),
];
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&envs,
));
assert_eq!(result["outcome"], "candidate");
for _ in 0..100 {
if reported.exists() {
break;
}
thread::sleep(Duration::from_millis(20));
}
assert_eq!(fs::read_to_string(&reported).unwrap(), "session-a");
let task = result["task_id"].as_str().unwrap();
json_output(&run(&home, &["accept", task], &envs));
}
/// The owner-surface registration must not trust an unverified pane either:
/// binding this session to another Codex's pane would push its notices there.
#[test]
fn an_unverified_pane_is_never_bound_to_a_codex_session() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
let reported = temp.path().join("reported-session");
fs::create_dir_all(&workspace).unwrap();
fs::write(&herdr, r#"#!/bin/sh
case "$1 $2" in
'agent get')
if [ -s "$BRGR_TEST_SESSION_FILE" ]; then
printf '{"result":{"agent":{"agent":"codex","pane_id":"w1:p1","agent_status":"working","agent_session":{"value":"%s"}}}}\n' "$(/bin/cat "$BRGR_TEST_SESSION_FILE")"
else
printf '%s\n' '{"result":{"agent":{"agent":"codex","pane_id":"w1:p1","agent_status":"working"}}}'
fi;;
'pane report-agent-session')
test "$3" = w1:p1 || exit 7
while [ "$#" -gt 0 ]; do
if [ "$1" = --source ]; then test "$2" = herdr:codex || exit 8; fi
if [ "$1" = --agent-session-id ]; then printf '%s' "$2" > "$BRGR_TEST_SESSION_FILE"; fi
shift
done
printf '%s\n' '{}';;
*) exit 9;;
esac
"#).unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let envs = [
("CODEX_THREAD_ID", "session-a"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-a"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("BRGR_TEST_SESSION_FILE", reported.to_str().unwrap()),
// A pane id this process does not run in, as a hook run by Codex's
// shared daemon inherits.
("BRGR_TEST_TRUST_HERDR_PANE", "0"),
];
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&envs,
));
assert_eq!(result["outcome"], "candidate");
thread::sleep(Duration::from_secs(2));
assert!(
!reported.exists(),
"a Codex session was bound to a pane this process does not run in"
);
let task = result["task_id"].as_str().unwrap();
json_output(&run(&home, &["accept", task], &envs));
}
#[test]
fn unavailable_herdr_does_not_hide_the_codex_inbox_hook() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
fs::create_dir_all(&workspace).unwrap();
fs::write(&herdr, "#!/bin/sh\nexec /bin/sleep 3\n").unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let owner = [
("BRGR_OWNER_ID", "codex:hook-fallback"),
("BRGR_SESSION_ID", "hook-fallback"),
];
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
let task = result["task_id"].as_str().unwrap();
let (output, elapsed) = invoke_hook_with_herdr(&home, &herdr, "user-prompt-submit");
assert!(
elapsed < Duration::from_secs(2),
"slow Herdr delayed the inbox hook: {elapsed:?}"
);
assert!(output.status.success());
let surfaced: Value = serde_json::from_slice(&output.stdout).unwrap();
assert!(
surfaced["hookSpecificOutput"]["additionalContext"]
.as_str()
.unwrap()
.contains(task)
);
let (stop, stop_elapsed) = invoke_hook_with_herdr(&home, &herdr, "stop");
assert!(stop_elapsed < Duration::from_secs(2));
let stop: Value = serde_json::from_slice(&stop.stdout).unwrap();
assert_eq!(stop["decision"], "block");
assert!(stop["reason"].as_str().unwrap().contains(task), "{stop}");
// Codex defines no hook-specific output for Stop and rejects the whole
// object when one is present.
assert!(stop.get("hookSpecificOutput").is_none(), "{stop}");
json_output(&run(&home, &["accept", task], &owner));
}
#[test]
fn slow_herdr_registration_cannot_leave_an_unclaimed_task() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
let reported = temp.path().join("reported-session");
fs::create_dir_all(&workspace).unwrap();
fs::write(&herdr, r#"#!/bin/sh
case "$1 $2" in
'agent get')
if [ -s "$BRGR_TEST_SESSION_FILE" ]; then
printf '{"result":{"agent":{"agent":"codex","pane_id":"w1:p1","agent_status":"working","agent_session":{"value":"%s"}}}}\n' "$(/bin/cat "$BRGR_TEST_SESSION_FILE")"
else
/bin/sleep 3
printf '%s\n' '{"result":{"agent":{"agent":"codex","pane_id":"w1:p1","agent_status":"working"}}}'
fi;;
'pane report-agent-session')
/bin/sleep 3
while [ "$#" -gt 0 ]; do
if [ "$1" = --agent-session-id ]; then printf '%s' "$2" > "$BRGR_TEST_SESSION_FILE"; fi
shift
done
printf '%s\n' '{}';;
*) exit 9;;
esac
"#).unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let envs = [
("CODEX_THREAD_ID", "session-a"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-a"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p1"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("BRGR_TEST_SESSION_FILE", reported.to_str().unwrap()),
];
let started = Instant::now();
let launch = json_output(&run(
&home,
&["run", "SLOW", "--workspace", workspace.to_str().unwrap()],
&envs,
));
assert!(
started.elapsed() < Duration::from_secs(2),
"task admission waited for Herdr"
);
let task = launch["task_id"].as_str().unwrap();
thread::sleep(Duration::from_millis(5_500));
let status = json_output(&run(&home, &["status", task], &envs));
assert_ne!(status["state"], "terminal", "{status}");
json_output(&run(&home, &["cancel", task], &envs));
let terminal = json_output(&run(
&home,
&["wait", task, "--timeout-seconds", "10"],
&envs,
));
assert_eq!(terminal["outcome"], "cancelled");
json_output(&run(&home, &["result", task, "--ack"], &envs));
thread::sleep(Duration::from_millis(800));
}
/// Codex runs every installed hook for a worker as well. brgr's hook is for
/// owners: in a task worktree it neither binds the session nor hands it the
/// owner's calling context.
#[test]
fn the_codex_hook_stays_out_of_a_worker_in_a_task_worktree() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let hook = |cwd: &Path| {
let mut child = brgr_command()
.arg("--home")
.arg(&home)
.args(["__hook", "session-start"])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap();
let input = json!({"session_id": "019b0000-0000-7000-8000-000000000001", "cwd": cwd});
child
.stdin
.take()
.unwrap()
.write_all(input.to_string().as_bytes())
.unwrap();
let output = child.wait_with_output().unwrap();
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap()
};
let owner = temp.path().join("project");
fs::create_dir_all(&owner).unwrap();
assert!(hook(&owner).contains("brgr calling context"));
let worker = home.join("worktrees/project/0123abcd");
fs::create_dir_all(&worker).unwrap();
assert_eq!(hook(&worker).trim(), "{}");
}
fn invoke_hook_with_herdr(
home: &Path,
herdr: &Path,
event: &str,
) -> (std::process::Output, Duration) {
let started = Instant::now();
let mut child = brgr_command()
.arg("--home")
.arg(home)
.args(["__hook", event])
.env("HERDR_ENV", "1")
.env("HERDR_PANE_ID", "w1:p1")
.env("HERDR_BIN_PATH", herdr)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap();
child
.stdin
.take()
.unwrap()
.write_all(b"{\"session_id\":\"hook-fallback\"}")
.unwrap();
let output = child.wait_with_output().unwrap();
(output, started.elapsed())
}
fn assert_duplicate_notification_dispatcher_exits(home: &Path, task: &str, envs: &[(&str, &str)]) {
let mut duplicate = brgr_command()
.arg("--home")
.arg(home)
.arg("__notify")
.arg(task)
.envs(envs.iter().copied())
.spawn()
.unwrap();
let duplicate_started = Instant::now();
while duplicate_started.elapsed() < Duration::from_secs(2) {
if duplicate.try_wait().unwrap().is_some() {
break;
}
thread::sleep(Duration::from_millis(20));
}
if duplicate.try_wait().unwrap().is_none() {
duplicate.kill().unwrap();
duplicate.wait().unwrap();
panic!("duplicate notification dispatcher stayed active");
}
}
#[test]
fn a_failed_run_is_reported_on_every_owner_command_until_acknowledged() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(&executable, "#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-failed-notice-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nfor item in \"$@\"; do case \"$item\" in @*) prompt=${item#@};; esac; done\nif /usr/bin/grep -q BRGR_FIXTURE_OK \"$prompt\"; then\n printf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"BRGR_FIXTURE_OK\"}]}}'\n printf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\nelse\n exit 7\nfi\n").unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [
("BRGR_OWNER_ID", "codex:failed-banner"),
("BRGR_SESSION_ID", "banner-session"),
];
let result = json_output(&run(
&home,
&[
"run",
"FAIL",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "failed");
let task = result["task_id"].as_str().unwrap();
let stderr_of =
|args: &[&str]| String::from_utf8_lossy(&run(&home, args, &owner).stderr).into_owned();
// Any later command of the owner carries the note, not only `result`.
for args in [&["status"][..], &["doctor"][..]] {
let text = stderr_of(args);
assert!(text.contains("failed or were lost"), "{args:?}: {text}");
assert!(text.contains(task), "{args:?}: {text}");
}
// Another owner is not told about it.
let other = [
("BRGR_OWNER_ID", "codex:someone-else"),
("BRGR_SESSION_ID", "other-session"),
];
assert!(!String::from_utf8_lossy(&run(&home, &["status"], &other).stderr).contains(task));
json_output(&run(&home, &["result", task, "--ack"], &owner));
let text = stderr_of(&["status"]);
assert!(!text.contains("failed or were lost"), "{text}");
}
#[test]
fn final_failed_result_does_not_leave_a_notification_dispatcher_running() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(&executable, "#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-failed-notice-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nfor item in \"$@\"; do case \"$item\" in @*) prompt=${item#@};; esac; done\nif /usr/bin/grep -q BRGR_FIXTURE_OK \"$prompt\"; then\n printf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"BRGR_FIXTURE_OK\"}]}}'\n printf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\nelse\n exit 7\nfi\n").unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:failed-notice")];
let result = json_output(&run(
&home,
&[
"run",
"FAIL",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "failed");
let task = result["task_id"].as_str().unwrap();
json_output(&run(&home, &["result", task, "--ack"], &owner));
let mut dispatcher = brgr_command()
.arg("--home")
.arg(&home)
.arg("__notify")
.arg(task)
.spawn()
.unwrap();
let started = Instant::now();
while started.elapsed() < Duration::from_secs(3) {
if dispatcher.try_wait().unwrap().is_some() {
return;
}
thread::sleep(Duration::from_millis(20));
}
dispatcher.kill().unwrap();
dispatcher.wait().unwrap();
panic!("final failed task kept its notification dispatcher alive");
}
#[test]
fn failed_execution_seals_requested_stderr() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let executable = temp.path().join("gjc");
fs::create_dir_all(&workspace).unwrap();
fs::write(&executable, r#"#!/bin/sh
case "$1" in
--version) echo 'gjc v-failed-log-fixture'; exit 0;;
--help) printf '%s\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;
esac
for item in "$@"; do case "$item" in @*) prompt=${item#@};; esac; done
if /usr/bin/grep -q BRGR_FIXTURE_OK "$prompt"; then
printf '%s\n' '{"type":"message_end","message":{"role":"assistant","content":[{"type":"text","text":"BRGR_FIXTURE_OK"}]}}'
printf '%s\n' '{"type":"agent_end","stopReason":"completed"}'
else
printf 'failure diagnostic 23\n' >&2
exit 23
fi
"#).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:failed-log")];
let result = json_output(&run(
&home,
&[
"run",
"FAIL",
"--workspace",
workspace.to_str().unwrap(),
"--capture-logs",
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "failed");
let detail = json_output(&run(
&home,
&["result", result["task_id"].as_str().unwrap()],
&owner,
));
assert_eq!(
detail["artifacts"][0]["reference"]["media_type"],
"text/x-brgr-stderr"
);
assert!(
detail["artifacts"][0]["text"]
.as_str()
.unwrap()
.contains("failure diagnostic 23")
);
}
#[test]
fn transferred_codex_session_receives_undecided_completion() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let herdr = temp.path().join("herdr");
let prompts = temp.path().join("prompts");
fs::create_dir_all(&workspace).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let task = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&[
("CODEX_THREAD_ID", "session-a"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-a"),
],
))["task_id"]
.as_str()
.unwrap()
.to_owned();
fs::write(
&herdr,
"#!/bin/sh\nif [ \"$1\" = --session ]; then shift 2; fi\ncase \"$1 $2\" in\n 'agent get') printf '%s\\n' '{\"result\":{\"agent\":{\"agent\":\"codex\",\"pane_id\":\"w1:p2\",\"agent_status\":\"idle\",\"agent_session\":{\"value\":\"session-b\"}}}}';;\n 'agent prompt') printf '%s\\n' \"$4\" >> \"$BRGR_TEST_PROMPTS\"; printf '{}\\n';;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let rebound = json_output(&run(
&home,
&["bind", &task, "--session", "session-b"],
&[
("CODEX_THREAD_ID", "session-b"),
("BRGR_OWNER_ID", "codex:session-a"),
("BRGR_SESSION_ID", "session-b"),
("HERDR_ENV", "1"),
("HERDR_PANE_ID", "w1:p2"),
("HERDR_BIN_PATH", herdr.to_str().unwrap()),
("BRGR_TEST_PROMPTS", prompts.to_str().unwrap()),
],
));
assert_eq!(rebound["session_id"], "session-b");
for _ in 0..100 {
if prompts.exists() {
break;
}
thread::sleep(Duration::from_millis(50));
}
let prompt = fs::read_to_string(prompts).expect("transferred Codex session was not prompted");
assert!(prompt.contains(&task));
assert!(prompt.contains("FROM BRGR"));
}
#[test]
fn owner_and_worker_exchange_questions_and_replies_during_one_attempt() {
let fixture = start_message_fixture();
let home = &fixture.home;
let task = fixture.task.as_str();
let attempt_text = fixture.attempt.as_str();
let owner = [("BRGR_OWNER_ID", "codex:message-test")];
let worker_owner = format!("worker:{attempt_text}");
let worker = [
("BRGR_OWNER_ID", worker_owner.as_str()),
("BRGR_SESSION_ID", worker_owner.as_str()),
("BRGR_PARENT_TASK_ID", task),
("BRGR_PARENT_ATTEMPT_ID", attempt_text),
];
worker_question_roundtrip(home, task, &owner, &worker);
let owner_question = json_output(&run(
home,
&[
"message",
"send",
task,
"--to",
"worker",
"--kind",
"question",
"--body",
"Confirm receipt?",
"--request-id",
"11111111-1111-4111-8111-111111111111",
],
&owner,
));
let owner_question_id = owner_question["message_id"].as_str().unwrap();
assert_message_replay_and_direction(home, task, &owner, owner_question_id);
assert_eq!(unanswered_message_count(home, task, attempt_text), 1);
json_output(&run(
home,
&["message", "ack", task, owner_question_id, "--for", "worker"],
&worker,
));
let worker_reply = json_output(&run(
home,
&[
"message",
"send",
task,
"--to",
"owner",
"--kind",
"reply",
"--reply-to",
owner_question_id,
"--body",
"Confirmed",
],
&worker,
));
assert_eq!(worker_reply["in_reply_to"], owner_question_id);
let owner_messages = json_output(&run(
home,
&["message", "list", task, "--for", "owner"],
&owner,
));
assert_eq!(owner_messages.as_array().unwrap().len(), 1);
assert_eq!(owner_messages[0]["body"], "Confirmed");
let reply_id = worker_reply["message_id"].as_str().unwrap();
json_output(&run(
home,
&["message", "ack", task, reply_id, "--for", "owner"],
&owner,
));
assert_eq!(unanswered_message_count(home, task, attempt_text), 0);
terminal_worker_can_read_and_ack_existing_message(home, task, &owner, &worker);
}
#[test]
fn omp_worker_delegates_to_gjc_then_gjc_without_pair_specific_routing() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let gjc = temp.path().join("gjc");
let omp = temp.path().join("omp");
fs::create_dir_all(&workspace).unwrap();
fs::write(&gjc, RECURSIVE_GJC_FIXTURE).unwrap();
let omp_fixture = RECURSIVE_GJC_FIXTURE
.replace("gjc v-recursive-fixture", "omp/fixture")
.replace(
"'--no-mcp' '--model' '--thinking'",
"'--no-prewalk' '--no-extensions' '--no-title' '--model=<value>' '--thinking=<value>'",
);
fs::write(&omp, omp_fixture).unwrap();
for executable in [&gjc, &omp] {
fs::set_permissions(executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(
&home,
executable,
&temp.path().join(format!(
"scratch-{}",
executable.file_name().unwrap().to_string_lossy()
)),
);
}
let root = json_output(&run(
&home,
&[
"run",
"ROOT",
"--harness",
"local.omp",
"--workspace",
workspace.to_str().unwrap(),
"--enable-delegation",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:mixed-root")],
));
assert_eq!(root["outcome"], "candidate");
let store = brgr_store::Store::open(home.join("store")).unwrap();
let tasks = store.tasks(10).unwrap();
assert_eq!(tasks.len(), 3);
let root_task = tasks.iter().find(|task| task.objective == "ROOT").unwrap();
assert_eq!(root_task.route.harness_id, "local.omp");
assert_eq!(
tasks
.iter()
.filter(|task| task.route.harness_id == "local.gjc")
.count(),
2
);
}
#[test]
fn plugin_codex_missing_binary_fails_visibly_without_launching() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let empty_bin = temp.path().join("empty-bin");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&empty_bin).unwrap();
let context = json!({"workspace_id": "w1", "workspace_cwd": workspace});
let launched = brgr_command()
.args(["plugin", "codex"])
.env("HERDR_ENV", "1")
.env("HERDR_PLUGIN_ID", "brgr")
.env("HERDR_PLUGIN_CONTEXT_JSON", context.to_string())
.env("BRGR_HOME", &home)
.env("CODEX_HOME", temp.path().join("codex-home"))
.env("PATH", &empty_bin)
.output()
.unwrap();
assert!(!launched.status.success());
assert!(String::from_utf8_lossy(&launched.stderr).contains("could not start Codex"));
}
#[test]
fn plugin_codex_uses_configured_executable_with_minimal_herdr_path() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let fake_bin = temp.path().join("bin");
let executable = fake_bin.join("codex");
let observed_path = temp.path().join("codex-path.txt");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&fake_bin).unwrap();
fs::write(
&executable,
"#!/bin/sh\nprintf '%s\\n' \"$PATH\" > \"$BRGR_TEST_CODEX_PATH\"\n/bin/sleep 0.2\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
let configured = json_output(&run(
&home,
&[
"config",
"set-codex-executable",
executable.to_str().unwrap(),
],
&[],
));
assert_eq!(
configured["herdr"]["codex_executable"],
executable.to_str().unwrap()
);
let context = json!({"workspace_id":"w1", "workspace_cwd":workspace});
let launched = brgr_command()
.args(["plugin", "codex"])
.env("HERDR_ENV", "1")
.env("HERDR_PLUGIN_ID", "brgr")
.env("HERDR_PLUGIN_CONTEXT_JSON", context.to_string())
.env("BRGR_HOME", &home)
.env("CODEX_HOME", temp.path().join("codex-home"))
.env("BRGR_TEST_CODEX_PATH", &observed_path)
.env("PATH", "/usr/bin:/bin")
.output()
.unwrap();
assert!(
launched.status.success(),
"{}",
String::from_utf8_lossy(&launched.stderr)
);
let passed_path = fs::read_to_string(observed_path).unwrap();
assert!(
passed_path
.split(':')
.any(|entry| entry == fake_bin.to_str().unwrap())
);
let cleared = json_output(&run(&home, &["config", "clear-codex-executable"], &[]));
assert!(cleared["herdr"]["codex_executable"].is_null());
assert!(
!run(
&home,
&["config", "set-codex-executable", "relative/codex"],
&[]
)
.status
.success()
);
}
#[test]
fn plugin_open_surfaces_herdr_launch_failure() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let fake_bin = temp.path().join("bin");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&fake_bin).unwrap();
let herdr = fake_bin.join("herdr");
fs::write(&herdr, "#!/bin/sh\necho 'pane refused' >&2\nexit 7\n").unwrap();
fs::set_permissions(&herdr, fs::Permissions::from_mode(0o700)).unwrap();
let context = json!({"workspace_id": "w1", "workspace_cwd": workspace});
let launched = brgr_command()
.args(["plugin", "open", "--no-focus", "--codex"])
.env("HERDR_ENV", "1")
.env("HERDR_PLUGIN_ID", "brgr")
.env("HERDR_PLUGIN_CONTEXT_JSON", context.to_string())
.env("HERDR_BIN_PATH", &herdr)
.env("BRGR_HOME", &home)
.output()
.unwrap();
assert!(!launched.status.success());
let stderr = String::from_utf8_lossy(&launched.stderr);
assert!(stderr.contains("could not open the brgr pane"));
assert!(stderr.contains("pane refused"));
}
#[test]
fn stale_codex_skill_or_hooks_report_drift_until_reinstall() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex");
let envs = [("CODEX_HOME", codex_home.to_str().unwrap())];
json_output(&run(&home, &["integrate", "codex", "install"], &envs));
assert_eq!(
json_output(&run(&home, &["integrate", "codex", "status"], &envs))["status"],
"installed"
);
let receipt_path = home.join("codex-integration.json");
let mut receipt: Value = serde_json::from_slice(&fs::read(&receipt_path).unwrap()).unwrap();
let skill_path = PathBuf::from(receipt["skill_path"].as_str().unwrap());
fs::write(&skill_path, "owned prior brgr skill\n").unwrap();
receipt["skill_text"] = json!("owned prior brgr skill\n");
fs::write(&receipt_path, serde_json::to_vec(&receipt).unwrap()).unwrap();
let stale_skill = json_output(&run(&home, &["integrate", "codex", "status"], &envs));
assert_eq!(stale_skill["status"], "drifted");
assert_eq!(stale_skill["current_skill"], false);
json_output(&run(&home, &["integrate", "codex", "install"], &envs));
let mut receipt: Value = serde_json::from_slice(&fs::read(&receipt_path).unwrap()).unwrap();
let old_command = receipt["commands"]["Stop"].as_str().unwrap().to_owned();
let drifted_command = format!("{old_command}-old");
receipt["commands"]["Stop"] = json!(drifted_command);
fs::write(&receipt_path, serde_json::to_vec(&receipt).unwrap()).unwrap();
let mut hooks: Value =
serde_json::from_slice(&fs::read(codex_home.join("hooks.json")).unwrap()).unwrap();
for entry in hooks["hooks"]["Stop"].as_array_mut().unwrap() {
if entry["hooks"][0]["command"] == old_command {
entry["hooks"][0]["command"] = json!(drifted_command);
}
}
fs::write(
codex_home.join("hooks.json"),
serde_json::to_vec(&hooks).unwrap(),
)
.unwrap();
let stale_hooks = json_output(&run(&home, &["integrate", "codex", "status"], &envs));
assert_eq!(stale_hooks["status"], "drifted");
assert_eq!(stale_hooks["current_hooks"], false);
json_output(&run(&home, &["integrate", "codex", "install"], &envs));
assert_eq!(
json_output(&run(&home, &["integrate", "codex", "status"], &envs))["status"],
"installed"
);
}
#[test]
fn claude_skill_installs_reports_drift_and_refuses_a_modified_copy() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let claude_home = temp.path().join("claude");
let envs = [("CLAUDE_CONFIG_DIR", claude_home.to_str().unwrap())];
let skill_path = claude_home.join("skills/brgr/SKILL.md");
let installed = json_output(&run(&home, &["integrate", "claude", "install"], &envs));
assert_eq!(installed["status"], "installed");
assert!(
fs::read_to_string(&skill_path)
.unwrap()
.contains("name: brgr")
);
assert_eq!(
json_output(&run(&home, &["integrate", "claude", "status"], &envs))["status"],
"installed"
);
fs::write(&skill_path, "hand-edited\n").unwrap();
assert_eq!(
json_output(&run(&home, &["integrate", "claude", "status"], &envs))["status"],
"drifted"
);
let refused = run(&home, &["integrate", "claude", "install"], &envs);
assert!(!refused.status.success());
assert!(String::from_utf8_lossy(&refused.stderr).contains("refusing to overwrite"));
assert_eq!(fs::read_to_string(&skill_path).unwrap(), "hand-edited\n");
let removed = json_output(&run(&home, &["integrate", "claude", "uninstall"], &envs));
assert_eq!(removed["skill_removed"], false);
assert!(skill_path.exists());
assert_eq!(
json_output(&run(&home, &["integrate", "claude", "status"], &envs))["status"],
"not_installed"
);
}
#[test]
fn codex_integration_accepts_an_equivalent_caller_and_detects_hook_binary_drift() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex");
let hook_binary = temp.path().join("installed-brgr");
fs::copy(brgr(), &hook_binary).unwrap();
fs::set_permissions(&hook_binary, fs::Permissions::from_mode(0o700)).unwrap();
let installed = Command::new(&hook_binary)
.arg("--home")
.arg(&home)
.arg("--json")
.args(["integrate", "codex", "install"])
.env("CODEX_HOME", &codex_home)
.output()
.unwrap();
assert!(installed.status.success());
let envs = [("CODEX_HOME", codex_home.to_str().unwrap())];
let equivalent = json_output(&run(&home, &["integrate", "codex", "status"], &envs));
assert_eq!(equivalent["status"], "installed");
assert_eq!(equivalent["current_hooks"], true);
fs::OpenOptions::new()
.append(true)
.open(&hook_binary)
.unwrap()
.write_all(b"drift")
.unwrap();
let drifted = json_output(&run(&home, &["integrate", "codex", "status"], &envs));
assert_eq!(drifted["status"], "drifted");
assert_eq!(drifted["current_hooks"], false);
}
#[test]
fn missing_plugin_bridge_fails_before_task_admission() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let output = brgr_command()
.arg("--home")
.arg(&home)
.args(["run", "BRGR_FIXTURE_OK"])
.env("BRGR_PLUGIN_BRIDGE_DIR", temp.path().join("missing"))
.output()
.unwrap();
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("bridge directory is unavailable"));
assert!(!home.join("store/brgr.sqlite3").exists());
}
#[test]
fn doctor_reports_changed_harness_instead_of_ok() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex");
let executable = temp.path().join("gjc");
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../testdata/fixtures/gjc");
fs::copy(fixture, &executable).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let envs = [("CODEX_HOME", codex_home.to_str().unwrap())];
json_output(&run(&home, &["integrate", "codex", "install"], &envs));
let healthy = json_output(&run(&home, &["doctor"], &envs));
assert_eq!(healthy["status"], "ok");
assert_eq!(healthy["harnesses"][0]["health"], "healthy");
writeln!(
fs::OpenOptions::new()
.append(true)
.open(&executable)
.unwrap(),
"# drift"
)
.unwrap();
let changed = run(&home, &["doctor"], &envs);
assert!(!changed.status.success());
let report: Value = serde_json::from_slice(&changed.stdout).unwrap();
assert_eq!(report["status"], "needs_attention");
assert_eq!(report["harnesses"][0]["id"], "local.gjc");
assert_eq!(report["harnesses"][0]["health"], "executable_changed");
let action = report["harnesses"][0]["action"].as_str().unwrap();
assert!(action.contains("re-certify"));
assert!(action.contains("brgr harness recertify local.gjc"));
let scratch = temp.path().join("scratch");
let recertified = run(
&home,
&[
"harness",
"recertify",
"local.gjc",
"--workspace",
scratch.to_str().unwrap(),
],
&envs,
);
assert!(recertified.status.success(), "{recertified:?}");
let healed = json_output(&run(&home, &["doctor"], &envs));
assert_eq!(healed["harnesses"][0]["health"], "healthy");
let dumped = String::from_utf8_lossy(&changed.stdout);
assert!(!dumped.contains("--mode=<value>"));
assert!(!dumped.contains("BRGR_FIXTURE_OK"));
}
/// A copy of the GJC fixture registered, then changed as an update would.
fn drifted_gjc_home() -> (TempDir, PathBuf, PathBuf) {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("workspace");
fs::create_dir_all(&workspace).unwrap();
let executable = temp.path().join("gjc");
fs::copy(gjc_fixture(), &executable).unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
writeln!(
fs::OpenOptions::new()
.append(true)
.open(&executable)
.unwrap(),
"# update"
)
.unwrap();
(temp, home, workspace)
}
#[test]
fn a_run_stops_on_an_updated_harness_unless_auto_recertify_is_on() {
let (_temp, home, workspace) = drifted_gjc_home();
let envs = [("BRGR_OWNER_ID", "codex:recertify-test")];
let args = [
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
];
let stopped = run(&home, &args, &envs);
assert!(!stopped.status.success());
let stderr = String::from_utf8_lossy(&stopped.stderr);
assert!(stderr.contains("executable_changed"), "{stderr}");
assert!(
stderr.contains("brgr harness recertify local.gjc"),
"{stderr}"
);
assert!(!stderr.contains("warning"), "{stderr}");
json_output(&run(
&home,
&["config", "set-auto-recertify", "true"],
&envs,
));
let created = run(&home, &args, &envs);
let stderr = String::from_utf8_lossy(&created.stderr);
assert!(
stderr.contains("warning: local.gjc changed (executable_changed"),
"{stderr}"
);
assert!(stderr.contains("set-auto-recertify false"), "{stderr}");
assert_eq!(json_output(&created)["outcome"], "candidate");
assert_eq!(
json_output(&run(&home, &["harness", "status", "local.gjc"], &envs))["health"],
"healthy"
);
}
#[test]
fn a_failed_automatic_recertification_still_stops_the_run() {
let (temp, home, workspace) = drifted_gjc_home();
let envs = [("BRGR_OWNER_ID", "codex:recertify-test")];
json_output(&run(
&home,
&["config", "set-auto-recertify", "true"],
&envs,
));
// The updated CLI no longer runs at all.
fs::write(temp.path().join("gjc"), "#!/bin/sh\nexit 1\n").unwrap();
let failed = run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&envs,
);
assert!(!failed.status.success());
let stderr = String::from_utf8_lossy(&failed.stderr);
assert!(
stderr.contains("automatic re-certification of local.gjc failed"),
"{stderr}"
);
}
#[test]
fn a_run_without_a_harness_uses_the_only_registered_one_and_refuses_to_pick() {
let (temp, home, workspace) = gjc_home();
let envs = [("BRGR_OWNER_ID", "codex:default-harness")];
let args = [
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
];
assert_eq!(
json_output(&run(&home, &args, &envs))["outcome"],
"candidate"
);
add_slow_prompt_file_harness(&home, &temp.path().join("slow"));
let refused = run(&home, &args, &envs);
assert!(!refused.status.success());
let stderr = String::from_utf8_lossy(&refused.stderr);
assert!(
stderr.contains("no harness named and no default configured"),
"{stderr}"
);
assert!(
stderr.contains("local.gjc") && stderr.contains("local.slow-agent"),
"{stderr}"
);
}
#[test]
fn named_process_harness_cannot_activate_without_authorized_scratch() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let fixture = gjc_fixture();
let denied = run(&home, &["harness", "add", fixture.to_str().unwrap()], &[]);
assert!(!denied.status.success());
assert!(!home.join("registry/activations/local.gjc.json").exists());
let unsafe_scratch = home.join("store");
fs::create_dir_all(&unsafe_scratch).unwrap();
let overlapping = run(
&home,
&[
"harness",
"add",
fixture.to_str().unwrap(),
"--workspace",
unsafe_scratch.to_str().unwrap(),
"--prompt",
"BRGR_FIXTURE_OK",
],
&[],
);
assert!(!overlapping.status.success());
assert!(String::from_utf8_lossy(&overlapping.stderr).contains("control directory"));
assert!(!home.join("registry/activations/local.gjc.json").exists());
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let health = json_output(&run(&home, &["harness", "status", "local.gjc"], &[]));
assert_eq!(health["health"], "healthy");
let activation_path = home.join("registry/activations/local.gjc.json");
let mut old_receipt: Value =
serde_json::from_slice(&fs::read(&activation_path).unwrap()).unwrap();
old_receipt["scratch_result_digest"] = Value::Null;
fs::write(&activation_path, serde_json::to_vec(&old_receipt).unwrap()).unwrap();
let workspace = temp.path().join("work");
fs::create_dir_all(&workspace).unwrap();
let blocked = run(
&home,
&[
"run",
"must not start",
"--workspace",
workspace.to_str().unwrap(),
],
&[("BRGR_OWNER_ID", "codex:old-activation")],
);
assert!(!blocked.status.success());
assert!(String::from_utf8_lossy(&blocked.stderr).contains("scratch certification"));
assert_eq!(
fs::read_dir(home.join("launches")).map_or(0, Iterator::count),
0
);
}
#[test]
fn herdr_unknown_model_stops_before_git_worktree_and_task_admission() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let binaries = temp.path().join("bin");
let repository = temp.path().join("repo");
fs::create_dir_all(&binaries).unwrap();
fs::create_dir_all(&repository).unwrap();
let omp = binaries.join("omp");
fs::write(
&omp,
"#!/bin/sh\ncase \"$1\" in models) echo '{\"models\":[]}';; *) exit 2;; esac\n",
)
.unwrap();
let launcher = binaries.join("omp-role");
fs::write(
&launcher,
"#!/bin/sh\necho '--expected-report --reuse-worktree-objective --reuse-worktree-owner --model --effort'\n",
)
.unwrap();
for executable in [&omp, &launcher] {
fs::set_permissions(executable, fs::Permissions::from_mode(0o700)).unwrap();
}
let path = format!("{}:/usr/bin:/bin", binaries.display());
json_output(&run(
&home,
&[
"harness",
"add",
launcher.to_str().unwrap(),
"--presentation-only",
],
&[("PATH", &path)],
));
assert!(
Command::new("git")
.args(["init", "-b", "main"])
.current_dir(&repository)
.output()
.unwrap()
.status
.success()
);
fs::write(repository.join("README"), b"clean fixture\n").unwrap();
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "add", "README"])
.output()
.unwrap()
.status
.success()
);
assert!(
Command::new("git")
.args([
"-C",
repository.to_str().unwrap(),
"-c",
"user.name=Fixture",
"-c",
"user.email=fixture@example.invalid",
"commit",
"-m",
"seed",
])
.output()
.unwrap()
.status
.success()
);
let denied = run(
&home,
&[
"run",
"MUST_NOT_START",
"--harness",
"local.omp-herdr",
"--model",
"fake/missing",
"--workspace",
repository.to_str().unwrap(),
],
&[("PATH", &path), ("BRGR_OWNER_ID", "codex:herdr-preflight")],
);
assert!(!denied.status.success());
assert!(
String::from_utf8_lossy(&denied.stderr).contains("absent from the current catalog"),
"{}",
String::from_utf8_lossy(&denied.stderr)
);
assert_eq!(fs::read_dir(home.join("launches")).unwrap().count(), 0);
assert_eq!(fs::read_dir(home.join("worktrees")).unwrap().count(), 0);
}
#[test]
fn installed_hooks_preserve_foreign_entries_and_uninstall_exact_ownership() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex");
fs::create_dir_all(&codex_home).unwrap();
let hooks_path = codex_home.join("hooks.json");
let original =
json!({"hooks":{"Stop":[{"hooks":[{"type":"command","command":"foreign-hook"}]}]}});
fs::write(&hooks_path, serde_json::to_vec(&original).unwrap()).unwrap();
let codex_home_value = codex_home.to_str().unwrap();
let envs = [("CODEX_HOME", codex_home_value)];
let first = json_output(&run(&home, &["integrate", "codex", "install"], &envs));
assert_eq!(first["hooks_added"], 3);
let second = json_output(&run(&home, &["integrate", "codex", "install"], &envs));
assert_eq!(second["hooks_added"], 0);
assert_eq!(
json_output(&run(&home, &["integrate", "codex", "status"], &envs))["status"],
"installed"
);
json_output(&run(&home, &["integrate", "codex", "uninstall"], &envs));
let after: Value = serde_json::from_slice(&fs::read(hooks_path).unwrap()).unwrap();
assert_eq!(after["hooks"]["Stop"], original["hooks"]["Stop"]);
assert!(!codex_home.join("skills/brgr/SKILL.md").exists());
}
#[test]
fn conflicting_skill_does_not_change_existing_hooks() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex");
let skill_dir = codex_home.join("skills/brgr");
fs::create_dir_all(&skill_dir).unwrap();
fs::write(skill_dir.join("SKILL.md"), b"user-owned skill\n").unwrap();
let hooks_path = codex_home.join("hooks.json");
fs::write(&hooks_path, b"{\"hooks\":{}}\n").unwrap();
let before = fs::read(&hooks_path).unwrap();
let output = run(
&home,
&["integrate", "codex", "install"],
&[("CODEX_HOME", codex_home.to_str().unwrap())],
);
assert!(!output.status.success());
assert_eq!(fs::read(&hooks_path).unwrap(), before);
assert_eq!(
fs::read(skill_dir.join("SKILL.md")).unwrap(),
b"user-owned skill\n"
);
}
#[test]
fn owned_old_skill_and_hook_upgrade_without_touching_foreign_hook() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let codex_home = temp.path().join("codex");
let envs = [("CODEX_HOME", codex_home.to_str().unwrap())];
let hooks_path = codex_home.join("hooks.json");
fs::create_dir_all(&codex_home).unwrap();
fs::write(
&hooks_path,
br#"{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"foreign-hook"}]}]}}"#,
)
.unwrap();
json_output(&run(&home, &["integrate", "codex", "install"], &envs));
let receipt_path = home.join("codex-integration.json");
let mut receipt: Value = serde_json::from_slice(&fs::read(&receipt_path).unwrap()).unwrap();
let skill_path = codex_home.join("skills/brgr/SKILL.md");
fs::write(&skill_path, "owned prior brgr skill\n").unwrap();
receipt["skill_text"] = json!("owned prior brgr skill\n");
let current_command = receipt["commands"]["Stop"].as_str().unwrap().to_owned();
let old_command = "owned-old-brgr-hook";
receipt["commands"]["Stop"] = json!(old_command);
fs::write(&receipt_path, serde_json::to_vec(&receipt).unwrap()).unwrap();
let mut hooks: Value = serde_json::from_slice(&fs::read(&hooks_path).unwrap()).unwrap();
for entry in hooks["hooks"]["Stop"].as_array_mut().unwrap() {
if entry["hooks"][0]["command"] == current_command {
entry["hooks"][0]["command"] = json!(old_command);
}
}
fs::write(&hooks_path, serde_json::to_vec(&hooks).unwrap()).unwrap();
json_output(&run(&home, &["integrate", "codex", "install"], &envs));
let updated: Value = serde_json::from_slice(&fs::read(&hooks_path).unwrap()).unwrap();
let commands = updated["hooks"]["Stop"]
.as_array()
.unwrap()
.iter()
.map(|entry| entry["hooks"][0]["command"].as_str().unwrap())
.collect::<Vec<_>>();
assert!(commands.contains(&"foreign-hook"));
assert!(commands.contains(¤t_command.as_str()));
assert!(!commands.contains(&old_command));
assert!(
fs::read_to_string(skill_path)
.unwrap()
.contains("brgr revise")
);
}
#[test]
fn real_cli_run_binds_candidate_to_its_owner() {
let (_temp, home, workspace) = gjc_home();
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--criterion",
"artifact text equals BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--deadline-seconds",
"5",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:owner-a")],
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let current = json_output(&run(
&home,
&["status", task],
&[("BRGR_OWNER_ID", "codex:owner-a")],
));
assert_eq!(
current["task"]["acceptance_criteria"][0],
"artifact text equals BRGR_FIXTURE_OK"
);
let readable = json_output(&run(
&home,
&["result", task],
&[("BRGR_OWNER_ID", "codex:owner-a")],
));
assert_eq!(readable["artifacts"][0]["text"], "BRGR_FIXTURE_OK");
let unreadable = run(
&home,
&["result", task],
&[("BRGR_OWNER_ID", "codex:owner-b")],
);
assert!(!unreadable.status.success());
let denied = run(
&home,
&["accept", task],
&[("BRGR_OWNER_ID", "codex:owner-b")],
);
assert!(!denied.status.success());
let accepted = json_output(&run(
&home,
&["accept", task, "--reason", "sealed fixture result checked"],
&[("BRGR_OWNER_ID", "codex:owner-a")],
));
assert_eq!(accepted["verdict"], "accepted");
let replay = json_output(&run(
&home,
&["accept", task, "--reason", "sealed fixture result checked"],
&[("BRGR_OWNER_ID", "codex:owner-a")],
));
assert_eq!(replay["decision_id"], accepted["decision_id"]);
}
#[test]
fn control_home_inside_worker_workspace_is_rejected_before_task_admission() {
let temp = TempDir::new().unwrap();
let workspace = temp.path().join("work");
let home = workspace.join("brgr-control");
fs::create_dir_all(&workspace).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let output = run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
],
&[("BRGR_OWNER_ID", "codex:home-overlap")],
);
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("must not overlap"));
let store = brgr_store::Store::open(home.join("store")).unwrap();
assert!(store.unstarted_tasks().unwrap().is_empty());
assert!(
store
.inbox(
&brgr_protocol::OwnerId::new("codex:home-overlap").unwrap(),
false
)
.unwrap()
.is_empty()
);
}
#[test]
fn unbound_task_needs_explicit_session_and_stale_session_cannot_decide() {
let (_temp, home, workspace) = gjc_home();
let unbound = [("BRGR_OWNER_ID", "codex:unbound"), ("BRGR_SESSION_ID", "")];
let first = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&unbound,
));
let task = first["task_id"].as_str().unwrap();
assert_eq!(first["outcome"], "candidate");
assert!(!run(&home, &["result", task], &unbound).status.success());
assert!(!run(&home, &["accept", task], &unbound).status.success());
let bound = json_output(&run(
&home,
&["bind", task, "--session", "session-a"],
&unbound,
));
let initial_epoch = bound["binding_epoch"].as_u64().unwrap();
let session_a = [
("BRGR_OWNER_ID", "codex:unbound"),
("BRGR_SESSION_ID", "session-a"),
];
assert_eq!(
json_output(&run(&home, &["result", task], &session_a))["artifacts"][0]["text"],
"BRGR_FIXTURE_OK"
);
let session_b = [("BRGR_SESSION_ID", "session-b")];
let rebound = json_output(&run(&home, &["bind", task], &session_b));
assert!(rebound["binding_epoch"].as_u64().unwrap() > initial_epoch);
assert!(!run(&home, &["result", task], &session_a).status.success());
assert!(!run(&home, &["accept", task], &session_a).status.success());
let accepted = json_output(&run(
&home,
&["accept", task, "--reason", "new session verified fixture"],
&session_b,
));
assert_eq!(accepted["verdict"], "accepted");
assert_eq!(accepted["session_id"], "session-b");
assert_eq!(accepted["binding_epoch"], rebound["binding_epoch"]);
}
#[test]
fn non_git_workspace_runs_without_a_git_executable() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("non-git");
fs::create_dir_all(&workspace).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&[
("BRGR_OWNER_ID", "codex:no-git"),
("PATH", "/bin:/usr/sbin:/sbin"),
],
));
assert_eq!(result["outcome"], "candidate");
}
#[test]
fn unsupported_model_fails_before_task_admission() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
fs::create_dir_all(&workspace).unwrap();
let executable = temp.path().join("prompt-only");
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'prompt-only 1';;\n --help) echo ' --prompt-file <path> fresh run';;\n --prompt-file) /bin/cat \"$2\";;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
json_output(&run(
&home,
&[
"harness",
"activate",
executable.to_str().unwrap(),
"--workspace",
workspace.to_str().unwrap(),
"--prompt",
"scratch",
],
&[],
));
let attempted = run(
&home,
&[
"run",
"should not launch",
"--harness",
"local.prompt-only",
"--model",
"gpt-5.6-luna",
"--workspace",
workspace.to_str().unwrap(),
],
&[],
);
assert!(!attempted.status.success());
assert!(String::from_utf8_lossy(&attempted.stderr).contains("model_select"));
assert!(
fs::read_dir(home.join("launches"))
.unwrap()
.next()
.is_none()
);
let owner = [("BRGR_OWNER_ID", "codex:generic-owner")];
let result = json_output(&run(
&home,
&[
"run",
"GENERIC_PROCESS_OK",
"--harness",
"local.prompt-only",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let sealed = json_output(&run(&home, &["result", task], &owner));
assert_worker_prompt(
sealed["artifacts"][0]["text"].as_str().unwrap(),
"GENERIC_PROCESS_OK",
);
let accepted = json_output(&run(
&home,
&["accept", task, "--reason", "generic result checked"],
&owner,
));
assert_eq!(accepted["verdict"], "accepted");
}
#[test]
fn missing_write_browser_and_mcp_capabilities_fail_before_task_admission() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:capability-preflight")];
for (flag, value, missing) in [
("--requires-write", None, "workspace_write"),
("--requires-browser", None, "browser"),
("--requires-mcp", Some("figma"), "mcp:figma"),
] {
let mut args = vec![
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
flag,
];
if let Some(value) = value {
args.push(value);
}
let rejected = run(&home, &args, &owner);
assert!(!rejected.status.success());
assert!(String::from_utf8_lossy(&rejected.stderr).contains(missing));
}
assert!(
fs::read_dir(home.join("launches"))
.unwrap()
.next()
.is_none()
);
assert!(
fs::read_dir(home.join("worktrees"))
.unwrap()
.next()
.is_none()
);
}
#[test]
fn devin_process_recipe_reaches_owner_acceptance_without_shell_interpolation() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let scratch = temp.path().join("scratch");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&scratch).unwrap();
let executable = temp.path().join("devin");
write_devin_fixture(&executable);
let activation = json_output(&run(
&home,
&[
"harness",
"add",
executable.to_str().unwrap(),
"--workspace",
scratch.to_str().unwrap(),
"--prompt",
"DEVIN_SCRATCH_OK",
],
&[],
));
assert_eq!(activation["harness_id"], "local.devin");
assert!(activation["scratch_result_digest"].as_str().is_some());
let health = json_output(&run(&home, &["harness", "status", "local.devin"], &[]));
assert_eq!(health["health"], "healthy");
let owner = [("BRGR_OWNER_ID", "codex:devin-owner")];
let explicit_model = run(
&home,
&[
"run",
"must not start",
"--harness",
"local.devin",
"--model",
"swe-1.6",
"--workspace",
workspace.to_str().unwrap(),
],
&owner,
);
assert!(!explicit_model.status.success());
assert!(String::from_utf8_lossy(&explicit_model.stderr).contains("model_select"));
assert_eq!(fs::read_dir(home.join("launches")).unwrap().count(), 0);
let result = json_output(&run(
&home,
&[
"run",
"DEVIN_PROCESS_OK",
"--harness",
"local.devin",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let detail = json_output(&run(&home, &["result", task], &owner));
assert_worker_prompt(
detail["artifacts"][0]["text"].as_str().unwrap(),
"DEVIN_PROCESS_OK",
);
assert_eq!(detail["route_observation"]["model_source"], "unavailable");
let accepted = json_output(&run(
&home,
&["accept", task, "--reason", "Devin fixture result verified"],
&owner,
));
assert_eq!(accepted["verdict"], "accepted");
}
#[test]
fn authored_manifest_runs_unknown_positional_cli_to_owner_acceptance() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
fs::create_dir_all(&workspace).unwrap();
let executable = temp.path().join("positional-agent");
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'positional 1';;\n --help) echo ' -p, --print prompt';;\n -p) printf '%s' \"$2\";;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
let manifest_path = temp.path().join("positional-manifest.json");
let manifest = json!({
"schema": "brgr.harness/v1",
"id": "local.positional-agent",
"adapter": "process/v1",
"executable": executable.canonicalize().unwrap(),
"probe": {"version_argv": ["--version"], "help_argv": ["--help"]},
"launch": {
"argv": ["-p", "${input.prompt}"],
"model_argv": [], "effort_argv": [],
"env_allow": ["HOME", "PATH"], "mode": "one_shot"
},
"result": {
"source": {"kind": "stdout"}, "media_type": "text/plain",
"max_bytes": 1024, "success_exit_codes": [0]
},
"capabilities": {
"completion": {
"status": "supported", "semantics": "process_exit",
"evidence_ref": "help", "tested_identity": null
}
}
});
fs::write(&manifest_path, serde_json::to_vec(&manifest).unwrap()).unwrap();
let path = manifest_path.to_str().unwrap();
let tested = json_output(&run(&home, &["harness", "test", "--manifest", path], &[]));
assert_eq!(tested["contract"], "passed");
json_output(&run(
&home,
&[
"harness",
"activate",
"--manifest",
path,
"--workspace",
workspace.to_str().unwrap(),
"--prompt",
"scratch",
],
&[],
));
let health = json_output(&run(
&home,
&["harness", "status", "local.positional-agent"],
&[],
));
assert_eq!(health["health"], "healthy");
let owner = [("BRGR_OWNER_ID", "codex:custom-manifest")];
let result = json_output(&run(
&home,
&[
"run",
"CUSTOM_MANIFEST_OK",
"--harness",
"local.positional-agent",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let sealed = json_output(&run(&home, &["result", task], &owner));
assert_worker_prompt(
sealed["artifacts"][0]["text"].as_str().unwrap(),
"CUSTOM_MANIFEST_OK",
);
let accepted = json_output(&run(
&home,
&["accept", task, "--reason", "custom result checked"],
&owner,
));
assert_eq!(accepted["verdict"], "accepted");
}
#[cfg(debug_assertions)]
#[test]
fn detached_supervisor_exit_before_claim_becomes_one_durable_lost_inbox_item() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:admission-crash")];
let launch = json_output(&run(
&home,
&[
"run",
"no model effect",
"--workspace",
workspace.to_str().unwrap(),
],
&[owner[0], ("BRGR_TEST_EXIT_BEFORE_TASK_CLAIM", "1")],
));
let task = launch["task_id"].as_str().unwrap();
let first_status = json_output(&run(&home, &["status", task], &owner));
assert_eq!(first_status["state"], "queued");
let mut terminal = None;
for _ in 0..120 {
let status = json_output(&run(&home, &["status", task], &owner));
if status["state"] == "terminal" {
terminal = Some(json_output(&run(&home, &["result", task], &owner)));
break;
}
thread::sleep(Duration::from_millis(50));
}
let terminal = terminal.expect("abandoned admitted task did not settle");
assert_eq!(terminal["result"]["outcome"], "lost");
assert_eq!(terminal["artifacts"].as_array().unwrap().len(), 0);
let result_id = terminal["result"]["result_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
assert_eq!(
store
.inbox(
&brgr_protocol::OwnerId::new("codex:admission-crash").unwrap(),
false
)
.unwrap()
.len(),
1
);
assert!(matches!(
store.claim_attempt(task.parse().unwrap(), 1, brgr_protocol::AttemptId::new()),
Err(brgr_store::StoreError::UnresolvedPriorAttempt { .. })
));
json_output(&run(&home, &["status", task], &owner));
let replay = json_output(&run(&home, &["result", task], &owner));
assert_eq!(replay["result"]["result_id"], result_id);
}
#[cfg(debug_assertions)]
#[test]
fn wait_reconciles_a_crashed_detached_supervisor_to_lost() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:wait-crash")];
let launch = json_output(&run(
&home,
&[
"run",
"crash before claim",
"--workspace",
workspace.to_str().unwrap(),
],
&[owner[0], ("BRGR_TEST_EXIT_BEFORE_TASK_CLAIM", "1")],
));
let task = launch["task_id"].as_str().unwrap();
let waited = json_output(&run(
&home,
&["wait", task, "--timeout-seconds", "8"],
&owner,
));
assert_eq!(waited["outcome"], "lost");
let result = json_output(&run(&home, &["result", task], &owner));
assert_eq!(waited["result_id"], result["result"]["result_id"]);
}
#[cfg(debug_assertions)]
#[test]
fn detached_crash_windows_reconcile_without_duplicate_or_overlapping_attempts() {
let fixture = gjc_fixture();
for (stage, expected) in [
("after_claim", "lost"),
("after_launch_intent", "lost"),
("after_spawn_before_pid", "lost"),
("after_seal_before_commit", "lost"),
("after_terminal_commit", "candidate"),
] {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
fs::create_dir_all(&workspace).unwrap();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:crash-window")];
let launch = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
],
&[owner[0], ("BRGR_TEST_CRASH_STAGE", stage)],
));
let task = launch["task_id"].as_str().unwrap();
let mut terminal = None;
for _ in 0..120 {
let status = json_output(&run(&home, &["status", task], &owner));
if status["state"] == "terminal" {
terminal = Some(json_output(&run(&home, &["result", task], &owner)));
break;
}
thread::sleep(Duration::from_millis(50));
}
let terminal = terminal.unwrap_or_else(|| panic!("crash stage {stage} did not settle"));
assert_eq!(terminal["result"]["outcome"], expected, "stage {stage}");
if expected == "lost" {
assert!(terminal["artifacts"].as_array().unwrap().is_empty());
assert!(
!terminal["result"]["unresolved_effects"]
.as_array()
.unwrap()
.is_empty()
);
} else {
assert_eq!(terminal["artifacts"][0]["text"], "BRGR_FIXTURE_OK");
}
let result_id = terminal["result"]["result_id"].as_str().unwrap();
let store = brgr_store::Store::open(home.join("store")).unwrap();
assert!(store.run_completed(result_id.parse().unwrap()).unwrap());
let owner_id = brgr_protocol::OwnerId::new("codex:crash-window").unwrap();
assert_eq!(
store.inbox(&owner_id, false).unwrap().len(),
1,
"stage {stage}"
);
assert!(
store
.claim_attempt(task.parse().unwrap(), 1, brgr_protocol::AttemptId::new())
.is_err()
);
json_output(&run(&home, &["status", task], &owner));
let replay = json_output(&run(&home, &["result", task], &owner));
assert_eq!(replay["result"]["result_id"], result_id, "stage {stage}");
}
}
#[cfg(debug_assertions)]
#[test]
fn restarted_supervisor_cannot_adopt_its_predecessors_unfinished_attempt() {
let (_temp, home, workspace) = gjc_home();
let owner = ("BRGR_OWNER_ID", "codex:restarted-supervisor");
let crashed = run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&[owner, ("BRGR_TEST_CRASH_STAGE", "after_claim")],
);
assert!(!crashed.status.success());
let launch_path = fs::read_dir(home.join("launches"))
.unwrap()
.map(|entry| entry.unwrap().path())
.find(|path| {
path.extension()
.is_some_and(|extension| extension == "json")
})
.unwrap();
let launch: Value = serde_json::from_slice(&fs::read(&launch_path).unwrap()).unwrap();
let task = launch["spec"]["task_id"].as_str().unwrap();
// The replacement process is allowed to reconcile the old attempt, but
// must not claim that its own PID proves the dead predecessor is alive.
let restarted = run(
&home,
&["__supervise", launch_path.to_str().unwrap()],
&[owner],
);
assert!(!restarted.status.success());
let store = brgr_store::Store::open(home.join("store")).unwrap();
let result = store.latest_result(task.parse().unwrap()).unwrap();
assert_eq!(result.outcome, brgr_protocol::TerminalOutcome::Lost);
let owner_id = brgr_protocol::OwnerId::new(owner.1).unwrap();
assert_eq!(store.inbox(&owner_id, false).unwrap().len(), 1);
assert!(store.unfinished_attempts().unwrap().is_empty());
}
#[cfg(debug_assertions)]
#[test]
fn status_during_live_pre_identity_window_does_not_publish_lost() {
let (_temp, home, workspace) = gjc_home();
let owner = ("BRGR_OWNER_ID", "codex:pre-identity");
let launch = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
workspace.to_str().unwrap(),
],
&[owner, ("BRGR_TEST_PAUSE_AFTER_CLAIM_MS", "1500")],
));
let task = launch["task_id"].as_str().unwrap();
let mut saw_window = false;
for _ in 0..100 {
let store = brgr_store::Store::open(home.join("store")).unwrap();
if store
.unfinished_attempts()
.unwrap()
.iter()
.any(|attempt| attempt.task.task_id.to_string() == task && attempt.launch.is_none())
{
let status = json_output(&run(&home, &["status", task], &[owner]));
assert_ne!(status["state"], "terminal");
saw_window = true;
break;
}
thread::sleep(Duration::from_millis(10));
}
assert!(saw_window, "the live pre-identity window was not observed");
let mut result = None;
for _ in 0..120 {
let status = json_output(&run(&home, &["status", task], &[owner]));
if status["state"] == "terminal" {
result = Some(json_output(&run(&home, &["result", task], &[owner])));
break;
}
thread::sleep(Duration::from_millis(20));
}
assert_eq!(
result.unwrap()["result"]["outcome"],
"candidate",
"a live supervisor was incorrectly recovered as lost"
);
}
#[test]
fn queued_cancel_settles_without_starting_the_harness() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:queued-cancel")];
let launch = json_output(&run(
&home,
&["run", "SLOW", "--workspace", workspace.to_str().unwrap()],
&[owner[0], ("BRGR_TEST_EXIT_BEFORE_TASK_CLAIM", "1")],
));
let task = launch["task_id"].as_str().unwrap();
let requested = json_output(&run(&home, &["cancel", task], &owner));
assert_eq!(requested["state"], "cancel_requested");
let mut outcome = None;
for _ in 0..120 {
let status = json_output(&run(&home, &["status", task], &owner));
if status["state"] == "terminal" {
outcome = Some(json_output(&run(&home, &["result", task], &owner)));
break;
}
thread::sleep(Duration::from_millis(50));
}
let result = outcome.expect("queued cancellation did not settle");
assert_eq!(result["result"]["outcome"], "cancelled");
assert!(result["artifacts"].as_array().unwrap().is_empty());
assert!(matches!(
brgr_store::Store::open(home.join("store"))
.unwrap()
.claim_attempt(task.parse().unwrap(), 1, brgr_protocol::AttemptId::new()),
Err(brgr_store::StoreError::NonRetryablePriorAttempt { .. })
));
}
#[test]
fn rejected_result_can_be_revised_without_rewriting_its_decision() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:revision-owner")];
let first = json_output(&run(
&home,
&[
"run",
"first draft",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
let task = first["task_id"].as_str().unwrap();
let original_result = first["result_id"].as_str().unwrap().to_owned();
assert_eq!(first["revision"], 1);
assert_eq!(first["outcome"], "candidate");
json_output(&run(
&home,
&["reject", task, "--reason", "needs correction"],
&owner,
));
let second = json_output(&run(
&home,
&[
"revise",
task,
"corrected draft",
"--criterion",
"the corrected draft is reviewable",
"--workspace",
workspace.to_str().unwrap(),
"--foreground",
],
&owner,
));
assert_eq!(second["task_id"], task);
assert_eq!(second["revision"], 2);
assert_eq!(second["outcome"], "candidate");
assert_ne!(second["result_id"], original_result);
assert!(home.join("launches").join(format!("{task}.json")).is_file());
assert!(
home.join("launches")
.join(format!("{task}-r2.json"))
.is_file()
);
let store = brgr_store::Store::open(home.join("store")).unwrap();
let first_id = original_result.parse().unwrap();
let original_decision = store.decision_for_result(first_id).unwrap().unwrap();
assert_eq!(
original_decision.verdict,
brgr_protocol::DecisionVerdict::Rejected
);
let latest = store.latest_result(task.parse().unwrap()).unwrap();
assert_eq!(
latest.result_id.to_string(),
second["result_id"].as_str().unwrap()
);
json_output(&run(
&home,
&["accept", task, "--reason", "corrected result checked"],
&owner,
));
assert_eq!(
store
.decision_for_result(first_id)
.unwrap()
.unwrap()
.verdict,
brgr_protocol::DecisionVerdict::Rejected
);
let after_accept = run(
&home,
&["revise", task, "a third draft", "--foreground"],
&owner,
);
assert!(!after_accept.status.success());
assert!(
!home
.join("launches")
.join(format!("{task}-r3.json"))
.exists()
);
}
#[test]
fn omp_process_can_cancel_without_a_herdr_pane() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
fs::create_dir_all(&workspace).unwrap();
let executable = temp.path().join("omp");
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'omp fixture 1'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-prewalk' '--no-extensions' '--no-title' '--model=<value>' '--thinking=<value>'; exit 0;;\nesac\nfor argument in \"$@\"; do case \"$argument\" in @*) prompt_file=${argument#@};; esac; done\nif /usr/bin/grep -q SLOW \"$prompt_file\"; then /bin/sleep 30; fi\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"OMP_FIXTURE_OK\"}]}}' '{\"type\":\"turn_end\",\"message\":{\"role\":\"assistant\"}}' '{\"type\":\"agent_end\"}'\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
json_output(&run(
&home,
&[
"harness",
"activate",
executable.to_str().unwrap(),
"--workspace",
workspace.to_str().unwrap(),
"--prompt",
"scratch",
],
&[],
));
let owner = [("BRGR_OWNER_ID", "codex:omp-cancel-test")];
let launch = json_output(&run(
&home,
&[
"run",
"SLOW",
"--harness",
"local.omp",
"--workspace",
workspace.to_str().unwrap(),
"--deadline-seconds",
"10",
],
&owner,
));
let task = launch["task_id"].as_str().unwrap();
for _ in 0..100 {
if run(&home, &["status", task], &owner).status.success() {
break;
}
thread::sleep(Duration::from_millis(50));
}
let requested = json_output(&run(&home, &["cancel", task], &owner));
assert_eq!(requested["state"], "cancel_requested");
let mut final_result = None;
for _ in 0..100 {
let output = run(&home, &["result", task], &owner);
if output.status.success() {
final_result = Some(json_output(&output));
break;
}
thread::sleep(Duration::from_millis(50));
}
assert_eq!(final_result.unwrap()["result"]["outcome"], "cancelled");
let acknowledged = json_output(&run(&home, &["result", task, "--ack"], &owner));
assert_eq!(acknowledged["result"]["outcome"], "cancelled");
}
#[test]
fn omp_fallback_model_cannot_be_sealed_as_requested_model() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let workspace = temp.path().join("work");
let scratch = temp.path().join("scratch");
fs::create_dir_all(&workspace).unwrap();
fs::create_dir_all(&scratch).unwrap();
let executable = temp.path().join("omp");
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'omp fixture 1'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-prewalk' '--no-extensions' '--no-title' '--model=<value>' '--thinking=<value>'; exit 0;;\n models) echo '{\"models\":[{\"selector\":\"other/fallback\"},{\"selector\":\"workbuddy/deepseek-v4.1-flash\"}]}'; exit 0;;\nesac\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"provider\":\"other\",\"model\":\"fallback\",\"content\":[{\"type\":\"text\",\"text\":\"SHOULD_NOT_ACCEPT\"}]}}' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
json_output(&run(
&home,
&[
"harness",
"add",
executable.to_str().unwrap(),
"--workspace",
scratch.to_str().unwrap(),
"--prompt",
"fixture",
],
&[],
));
let owner = [("BRGR_OWNER_ID", "codex:wrong-model")];
let unknown = run(
&home,
&[
"run",
"unknown model must not start",
"--harness",
"local.omp",
"--workspace",
workspace.to_str().unwrap(),
"--model",
"unknown/model",
],
&owner,
);
assert!(!unknown.status.success());
assert!(String::from_utf8_lossy(&unknown.stderr).contains("absent from the current catalog"));
assert_eq!(fs::read_dir(home.join("launches")).unwrap().count(), 0);
assert_eq!(fs::read_dir(home.join("worktrees")).unwrap().count(), 0);
let result = json_output(&run(
&home,
&[
"run",
"must use WorkBuddy",
"--harness",
"local.omp",
"--workspace",
workspace.to_str().unwrap(),
"--model",
"workbuddy/deepseek-v4.1-flash",
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "failed");
assert!(result["artifacts"].as_array().unwrap().is_empty());
assert!(result["error"].as_str().unwrap().contains("other/fallback"));
let task = result["task_id"].as_str().unwrap();
assert!(!run(&home, &["accept", task], &owner).status.success());
let matched = json_output(&run(
&home,
&[
"run",
"record native route",
"--harness",
"local.omp",
"--workspace",
workspace.to_str().unwrap(),
"--model",
"other/fallback",
"--foreground",
],
&owner,
));
assert_eq!(matched["outcome"], "candidate");
let detail = json_output(&run(
&home,
&["result", matched["task_id"].as_str().unwrap()],
&owner,
));
assert_eq!(detail["route_observation"]["model"], "other/fallback");
assert_eq!(detail["route_observation"]["model_source"], "harness_jsonl");
assert_eq!(detail["route_observation"]["effort_source"], "unavailable");
}
#[test]
fn detached_success_reopens_for_an_offline_owner_then_accepts_once() {
let (_temp, home, workspace) = gjc_home();
let owner = [("BRGR_OWNER_ID", "codex:offline-owner")];
let launch = json_output(&run(
&home,
&[
"run",
"DETACHED_OWNER_OFFLINE_OK",
"--workspace",
workspace.to_str().unwrap(),
],
&owner,
));
let task = launch["task_id"].as_str().unwrap().to_owned();
assert_eq!(launch["state"], "starting");
let mut terminal = None;
for _ in 0..120 {
let status = json_output(&run(&home, &["status", &task], &owner));
if status["state"] == "terminal" {
terminal = Some(json_output(&run(&home, &["result", &task], &owner)));
break;
}
thread::sleep(Duration::from_millis(50));
}
let terminal = terminal.expect("detached fixture run did not settle for its offline owner");
assert_eq!(terminal["result"]["outcome"], "candidate");
assert_eq!(terminal["artifacts"][0]["text"], "BRGR_FIXTURE_OK");
let result_id = terminal["result"]["result_id"].as_str().unwrap().to_owned();
let owner_id = brgr_protocol::OwnerId::new("codex:offline-owner").unwrap();
let pending = brgr_store::Store::open(home.join("store"))
.unwrap()
.inbox(&owner_id, false)
.unwrap();
assert_eq!(pending.len(), 1);
assert!(!pending[0].acknowledged);
assert_eq!(pending[0].result.result_id.to_string(), result_id);
let replay = json_output(&run(&home, &["result", &task], &owner));
assert_eq!(replay["result"]["result_id"], result_id);
let still_pending = brgr_store::Store::open(home.join("store"))
.unwrap()
.inbox(&owner_id, false)
.unwrap();
assert_eq!(still_pending.len(), 1);
assert_eq!(still_pending[0].result.result_id.to_string(), result_id);
let accepted = json_output(&run(
&home,
&[
"accept",
&task,
"--reason",
"detached fixture output verified",
],
&owner,
));
assert_eq!(accepted["verdict"], "accepted");
assert_eq!(accepted["result_id"], result_id);
let store = brgr_store::Store::open(home.join("store")).unwrap();
let decision = store
.decision_for_result(result_id.parse().unwrap())
.unwrap()
.expect("accepted result has no recorded decision");
assert_eq!(decision.verdict, brgr_protocol::DecisionVerdict::Accepted);
assert_eq!(decision.result_id.to_string(), result_id);
assert_eq!(
serde_json::to_value(&decision).unwrap()["decision_id"],
accepted["decision_id"]
);
assert!(store.inbox(&owner_id, false).unwrap().is_empty());
let acknowledged = store.inbox(&owner_id, true).unwrap();
assert_eq!(acknowledged.len(), 1);
assert!(acknowledged[0].acknowledged);
assert_eq!(acknowledged[0].result.result_id.to_string(), result_id);
}
#[test]
fn dirty_source_is_rejected_before_creating_a_task_worktree() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
let initialized = Command::new("git")
.arg("-C")
.arg(&repository)
.args(["init", "-b", "main"])
.output()
.unwrap();
assert!(initialized.status.success());
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
fs::write(repository.join("user-note.txt"), b"uncommitted work\n").unwrap();
let rejected = run(
&home,
&["run", "check", "--workspace", repository.to_str().unwrap()],
&[("BRGR_OWNER_ID", "codex:dirty-test")],
);
assert!(!rejected.status.success());
assert!(
String::from_utf8_lossy(&rejected.stderr).contains("uncommitted changes"),
"{}",
String::from_utf8_lossy(&rejected.stderr)
);
assert!(!home.join("worktrees/repo").exists());
assert_eq!(
fs::read_to_string(repository.join("user-note.txt")).unwrap(),
"uncommitted work\n"
);
let branches = Command::new("git")
.args([
"-C",
repository.to_str().unwrap(),
"branch",
"--list",
"brgr/*",
])
.output()
.unwrap();
assert!(branches.status.success());
assert!(String::from_utf8_lossy(&branches.stdout).trim().is_empty());
}
#[test]
fn selected_dirty_files_are_copied_without_touching_other_source_changes() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
fs::write(repository.join("README"), "selected edit\n").unwrap();
fs::write(repository.join("selected.txt"), "new selected file\n").unwrap();
fs::write(repository.join("private-note.txt"), "keep at source\n").unwrap();
let owner = [("BRGR_OWNER_ID", "codex:selected-dirty")];
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
repository.to_str().unwrap(),
"--snapshot-path",
"README",
"--snapshot-path",
"selected.txt",
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let task_workspace = brgr_store::Store::open(home.join("store"))
.unwrap()
.task(task.parse().unwrap())
.unwrap()
.workspace;
assert_eq!(
fs::read_to_string(Path::new(&task_workspace).join("README")).unwrap(),
"selected edit\n"
);
assert_eq!(
fs::read_to_string(Path::new(&task_workspace).join("selected.txt")).unwrap(),
"new selected file\n"
);
assert!(!Path::new(&task_workspace).join("private-note.txt").exists());
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"selected edit\n"
);
assert_eq!(
fs::read_to_string(repository.join("private-note.txt")).unwrap(),
"keep at source\n"
);
let receipt: Value = serde_json::from_slice(
&fs::read(home.join("runs").join(format!("{task}-r1.snapshot.json"))).unwrap(),
)
.unwrap();
assert_eq!(receipt["entries"].as_array().unwrap().len(), 2);
assert_eq!(receipt["entries"][1]["path"], "selected.txt");
let rejected = run(
&home,
&[
"run",
"bad path",
"--workspace",
repository.to_str().unwrap(),
"--snapshot-path",
"../secret",
],
&owner,
);
assert!(!rejected.status.success());
}
#[test]
fn selected_tracked_deletion_is_reproduced_in_task_worktree() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
fs::write(repository.join("remove.txt"), "tracked\n").unwrap();
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "add", "remove.txt"])
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.args([
"-C",
repository.to_str().unwrap(),
"commit",
"-m",
"add deletion fixture"
])
.status()
.unwrap()
.success()
);
fs::remove_file(repository.join("remove.txt")).unwrap();
let fixture = gjc_fixture();
add_fixture(&home, &fixture, &temp.path().join("scratch"));
let result = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
repository.to_str().unwrap(),
"--snapshot-path",
"remove.txt",
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:selected-delete")],
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let workspace = brgr_store::Store::open(home.join("store"))
.unwrap()
.task(task.parse().unwrap())
.unwrap()
.workspace;
assert!(!Path::new(&workspace).join("remove.txt").exists());
let receipt: Value = serde_json::from_slice(
&fs::read(home.join("runs").join(format!("{task}-r1.snapshot.json"))).unwrap(),
)
.unwrap();
assert_eq!(receipt["entries"][0]["action"], "delete");
}
fn integration_conflict_preserves_owner_edit(
home: &Path,
repository: &Path,
task: &str,
owner: &[(&str, &str)],
) {
let subdirectory = repository.join("subdirectory");
fs::create_dir(&subdirectory).unwrap();
let partial = run(
home,
&["apply", task, "--workspace", subdirectory.to_str().unwrap()],
owner,
);
assert!(!partial.status.success());
assert!(String::from_utf8_lossy(&partial.stderr).contains("repository root"));
fs::write(repository.join("README"), "conflicting owner edit\n").unwrap();
assert!(
!run(
home,
&["apply", task, "--workspace", repository.to_str().unwrap()],
owner,
)
.status
.success()
);
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"conflicting owner edit\n"
);
fs::write(repository.join("README"), "seed\n").unwrap();
}
fn assert_png_artifact_exports(home: &Path, output: &Path, task: &str, owner: &[(&str, &str)]) {
json_output(&run(
home,
&[
"artifact",
"export",
task,
"5",
"--output",
output.to_str().unwrap(),
],
owner,
));
assert_eq!(fs::read(output).unwrap(), b"PNG_BYTES");
}
fn assert_tree_waits_for_owner_approval(home: &Path, task: &str, owner: &[(&str, &str)]) {
let status = json_output(&run(home, &["status", task, "--tree"], owner));
assert_eq!(status["nodes"][0]["waiting_for"], "approval");
}
fn configure_misleading_external_diff(repository: &Path, script: &Path) {
fs::write(script, "#!/bin/sh\nprintf 'NOT_A_PATCH\\n'\n").unwrap();
fs::set_permissions(script, fs::Permissions::from_mode(0o700)).unwrap();
assert!(
Command::new("git")
.arg("-C")
.arg(repository)
.args(["config", "diff.external"])
.arg(script)
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.arg("-C")
.arg(repository)
.args(["config", "diff.noprefix", "true"])
.status()
.unwrap()
.success()
);
}
fn assert_sealed_patch_ignores_external_driver(detail: &Value) {
let patch = detail["artifacts"][1]["text"].as_str().unwrap();
assert!(patch.contains("diff --git"));
assert!(!patch.contains("NOT_A_PATCH"));
}
#[test]
fn sealed_evidence_is_reviewed_before_separate_conflict_checked_integration() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
let executable = temp.path().join("gjc");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
configure_misleading_external_diff(&repository, &temp.path().join("external-diff"));
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-evidence-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nfor item in \"$@\"; do case \"$item\" in @*) prompt=${item#@};; esac; done\nif /usr/bin/grep -q EVIDENCE_TASK \"$prompt\"; then\n printf 'changed\\n' > README\n printf 'added\\n' > added.txt\n printf 'report\\n' > report.md\n printf 'PNG_BYTES' > screen.png\n printf 'worker log\\n' >&2\nfi\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"EVIDENCE_OK\"}]}}'\nprintf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:evidence")];
let outcome = json_output(&run(
&home,
&[
"run",
"EVIDENCE_TASK",
"--workspace",
repository.to_str().unwrap(),
"--capture-diff",
"--capture-logs",
"--evidence-file",
"report.md",
"--evidence-file",
"screen.png",
"--foreground",
],
&owner,
));
assert_eq!(outcome["outcome"], "candidate");
let task = outcome["task_id"].as_str().unwrap();
let detail = json_output(&run(&home, &["result", task], &owner));
assert_eq!(detail["artifacts"].as_array().unwrap().len(), 6);
assert_sealed_patch_ignores_external_driver(&detail);
assert_tree_waits_for_owner_approval(&home, task, &owner);
assert_eq!(
detail["artifacts"][1]["reference"]["media_type"],
"text/x-diff"
);
assert_eq!(
detail["artifacts"][5]["reference"]["media_type"],
"image/png"
);
assert!(detail["artifacts"][5]["text"].is_null());
assert_png_artifact_exports(&home, &temp.path().join("export.png"), task, &owner);
integration_conflict_preserves_owner_edit(&home, &repository, task, &owner);
let check = json_output(&run(
&home,
&["apply", task, "--workspace", repository.to_str().unwrap()],
&owner,
));
assert_eq!(check["status"], "ready");
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"seed\n"
);
assert!(
!run(
&home,
&[
"apply",
task,
"--workspace",
repository.to_str().unwrap(),
"--execute"
],
&owner,
)
.status
.success()
);
json_output(&run(&home, &["accept", task], &owner));
let applied = json_output(&run(
&home,
&[
"apply",
task,
"--workspace",
repository.to_str().unwrap(),
"--execute",
],
&owner,
));
assert_eq!(applied["status"], "applied");
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"changed\n"
);
assert_eq!(
fs::read_to_string(repository.join("added.txt")).unwrap(),
"added\n",
"a file the worker created without staging must reach the owner"
);
assert!(!repository.join("report.md").exists());
}
#[test]
fn selected_input_is_excluded_from_worker_patch_and_source_edit_survives_apply() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
let executable = temp.path().join("gjc");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
fs::write(repository.join("worker.txt"), "base\n").unwrap();
fs::write(repository.join(".gitattributes"), "*.txt text eol=crlf\n").unwrap();
assert!(
Command::new("git")
.arg("-C")
.arg(&repository)
.args(["add", "worker.txt", ".gitattributes"])
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.arg("-C")
.arg(&repository)
.args(["commit", "-m", "worker fixture"])
.status()
.unwrap()
.success()
);
fs::write(repository.join("README"), "selected source edit\n").unwrap();
fs::write(repository.join("selected.txt"), b"new source file\r\n").unwrap();
fs::write(&executable, "#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-snapshot-patch-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nprintf 'worker edit\\n' > worker.txt\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"OK\"}]}}'\nprintf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\n").unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:snapshot-patch")];
let result = json_output(&run(
&home,
&[
"run",
"worker patch",
"--workspace",
repository.to_str().unwrap(),
"--snapshot-path",
"README",
"--snapshot-path",
"selected.txt",
"--capture-diff",
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let detail = json_output(&run(&home, &["result", task], &owner));
let patch = detail["artifacts"][1]["text"].as_str().unwrap();
assert!(patch.contains("worker.txt"));
assert!(!patch.contains("README"));
assert!(!patch.contains("selected.txt"));
json_output(&run(&home, &["accept", task], &owner));
let applied = json_output(&run(
&home,
&[
"apply",
task,
"--workspace",
repository.to_str().unwrap(),
"--execute",
],
&owner,
));
assert_eq!(applied["status"], "applied");
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"selected source edit\n"
);
assert_eq!(
fs::read_to_string(repository.join("selected.txt")).unwrap(),
"new source file\r\n"
);
assert_eq!(
fs::read_to_string(repository.join("worker.txt")).unwrap(),
"worker edit\r\n"
);
}
#[test]
fn committed_worker_change_uses_the_task_start_commit_for_diff_and_apply() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
let executable = temp.path().join("gjc");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
fs::write(&executable, "#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-commit-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nprintf 'committed worker edit\\n' > README\n/usr/bin/git add README\n/usr/bin/git -c user.name=Worker -c user.email=worker@example.test commit -m worker-edit >/dev/null\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"OK\"}]}}'\nprintf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\n").unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:committed-patch")];
let result = json_output(&run(
&home,
&[
"run",
"commit the change",
"--workspace",
repository.to_str().unwrap(),
"--capture-diff",
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let detail = json_output(&run(&home, &["result", task], &owner));
assert!(
detail["artifacts"][1]["text"]
.as_str()
.unwrap()
.contains("committed worker edit")
);
json_output(&run(&home, &["accept", task], &owner));
let applied = json_output(&run(
&home,
&[
"apply",
task,
"--workspace",
repository.to_str().unwrap(),
"--execute",
],
&owner,
));
assert_eq!(applied["status"], "applied");
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"committed worker edit\n"
);
}
#[test]
fn subdirectory_snapshot_uses_repository_relative_paths() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
let subdirectory = repository.join("sub");
let executable = temp.path().join("gjc");
fs::create_dir_all(&subdirectory).unwrap();
seed_git_repo(&repository);
fs::write(subdirectory.join("a"), "base\n").unwrap();
assert!(
Command::new("git")
.arg("-C")
.arg(&repository)
.args(["add", "sub/a"])
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.arg("-C")
.arg(&repository)
.args(["commit", "-m", "sub fixture"])
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.arg("-C")
.arg(&repository)
.args(["config", "diff.relative", "true"])
.status()
.unwrap()
.success()
);
fs::write(subdirectory.join("a"), "selected sub edit\n").unwrap();
fs::write(&executable, "#!/bin/sh\ncase \"$1\" in\n --version) echo 'gjc v-subdir-fixture'; exit 0;;\n --help) printf '%s\\n' '-p, --print' '--mode=<value>' '--no-session' '--no-mcp' '--model' '--thinking'; exit 0;;\nesac\nprintf 'worker root edit\\n' > ../README\nprintf '%s\\n' '{\"type\":\"message_end\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"OK\"}]}}'\nprintf '%s\\n' '{\"type\":\"agent_end\",\"stopReason\":\"completed\"}'\n").unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
add_fixture(&home, &executable, &temp.path().join("scratch"));
let owner = [("BRGR_OWNER_ID", "codex:subdir-snapshot")];
let result = json_output(&run(
&home,
&[
"run",
"subdir snapshot",
"--workspace",
subdirectory.to_str().unwrap(),
"--snapshot-path",
"a",
"--capture-diff",
"--foreground",
],
&owner,
));
assert_eq!(result["outcome"], "candidate");
let task = result["task_id"].as_str().unwrap();
let detail = json_output(&run(&home, &["result", task], &owner));
let patch = detail["artifacts"][1]["text"].as_str().unwrap();
assert!(patch.contains("README"), "sealed patch: {patch}");
assert!(!patch.contains("sub/a"), "sealed patch: {patch}");
json_output(&run(&home, &["accept", task], &owner));
let applied = json_output(&run(
&home,
&[
"apply",
task,
"--workspace",
repository.to_str().unwrap(),
"--execute",
],
&owner,
));
assert_eq!(applied["status"], "applied");
assert_eq!(
fs::read_to_string(subdirectory.join("a")).unwrap(),
"selected sub edit\n"
);
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"worker root edit\n"
);
}
fn seed_git_repo(repository: &Path) {
assert!(
Command::new("git")
.args(["init", "-b", "main"])
.current_dir(repository)
.output()
.unwrap()
.status
.success()
);
for (key, value) in [
("user.name", "Fixture"),
("user.email", "fixture@example.invalid"),
] {
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "config", key, value])
.status()
.unwrap()
.success()
);
}
fs::write(repository.join("README"), b"seed\n").unwrap();
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "add", "README"])
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "commit", "-m", "seed"])
.status()
.unwrap()
.success()
);
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "branch", "user/keep-me"])
.status()
.unwrap()
.success()
);
}
fn git_branches(repository: &Path) -> String {
let output = Command::new("git")
.args(["-C", repository.to_str().unwrap(), "branch", "--list"])
.output()
.unwrap();
assert!(output.status.success());
String::from_utf8_lossy(&output.stdout).into_owned()
}
#[test]
fn concurrent_git_worktree_admissions_keep_distinct_identities() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
add_fixture(&home, &gjc_fixture(), &temp.path().join("scratch"));
let repo = repository.to_str().unwrap().to_owned();
let home_path = home.clone();
thread::scope(|scope| {
let left = scope.spawn(|| {
run(
&home_path,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
&repo,
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:worktree-left")],
)
});
let right = scope.spawn(|| {
run(
&home_path,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
&repo,
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:worktree-right")],
)
});
let left = json_output(&left.join().unwrap());
let right = json_output(&right.join().unwrap());
assert_eq!(left["outcome"], "candidate");
assert_eq!(right["outcome"], "candidate");
assert_ne!(left["task_id"], right["task_id"]);
assert_ne!(left["result_id"], right["result_id"]);
});
let listed = git_branches(&repository);
assert!(listed.contains("user/keep-me"));
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"seed\n"
);
let created = fs::read_dir(home.join("worktrees/repo")).unwrap().count();
assert_eq!(created, 2);
}
#[test]
fn worktree_created_before_admission_is_preserved_and_not_reused() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
add_fixture(&home, &gjc_fixture(), &temp.path().join("scratch"));
let crashed = run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
repository.to_str().unwrap(),
],
&[
("BRGR_OWNER_ID", "codex:worktree-gap"),
("BRGR_TEST_EXIT_AFTER_WORKTREE", "1"),
],
);
assert_eq!(crashed.status.code(), Some(78));
let leftover = fs::read_dir(home.join("worktrees/repo")).unwrap();
let leftovers: Vec<_> = leftover.map(|entry| entry.unwrap().path()).collect();
assert_eq!(leftovers.len(), 1);
assert!(leftovers[0].join("README").is_file());
let store = brgr_store::Store::open(home.join("store")).unwrap();
assert!(store.unstarted_tasks().unwrap().is_empty());
assert_eq!(
home.join("launches").read_dir().map_or(0, Iterator::count),
0
);
let listed = git_branches(&repository);
assert!(listed.contains("user/keep-me"));
assert!(listed.contains("brgr/task-"));
let recovered = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
repository.to_str().unwrap(),
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:worktree-gap")],
));
assert_eq!(recovered["outcome"], "candidate");
assert_eq!(
fs::read_dir(home.join("worktrees/repo")).unwrap().count(),
2,
"orphan worktree must be kept and a new admission must use a new path"
);
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"seed\n"
);
}
#[test]
fn missing_task_worktree_fails_closed_without_recreate_or_new_identity() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
add_fixture(&home, &gjc_fixture(), &temp.path().join("scratch"));
let started = json_output(&run(
&home,
&[
"run",
"BRGR_FIXTURE_OK",
"--workspace",
repository.to_str().unwrap(),
],
&[
("BRGR_OWNER_ID", "codex:worktree-missing"),
("BRGR_TEST_EXIT_BEFORE_TASK_CLAIM", "1"),
],
));
let task = started["task_id"].as_str().unwrap();
let workspace = Path::new(started["workspace"].as_str().unwrap());
assert!(workspace.is_dir());
fs::remove_dir_all(workspace).unwrap();
assert!(
Command::new("git")
.args(["-C", repository.to_str().unwrap(), "worktree", "prune"])
.status()
.unwrap()
.success()
);
let status = json_output(&run(
&home,
&["status", task],
&[("BRGR_OWNER_ID", "codex:worktree-missing")],
));
assert_eq!(status["task"]["task_id"], task);
assert_eq!(status["workspace_present"], false);
assert_eq!(status["state"], "terminal");
let result = json_output(&run(
&home,
&["result", task],
&[("BRGR_OWNER_ID", "codex:worktree-missing")],
));
assert_eq!(result["result"]["outcome"], "lost");
assert_eq!(result["result"]["task_id"], task);
assert!(
result["result"]["error"]
.as_str()
.unwrap()
.contains("task worktree is missing")
);
assert!(!workspace.exists());
assert!(git_branches(&repository).contains("user/keep-me"));
let store = brgr_store::Store::open(home.join("store")).unwrap();
assert_eq!(store.unstarted_tasks().unwrap().len(), 0);
}
fn add_slow_prompt_file_harness(home: &Path, root: &Path) {
fs::create_dir_all(root.join("scratch")).unwrap();
let executable = root.join("slow-agent");
fs::write(
&executable,
"#!/bin/sh\ncase \"$1\" in\n --version) echo 'slow 1';;\n --help) echo ' --prompt-file <path> fresh run';;\n --prompt-file)\n if /usr/bin/grep -q SLOW_RUN \"$2\"; then /bin/sleep 4; fi\n /bin/cat \"$2\";;\n *) exit 2;;\nesac\n",
)
.unwrap();
fs::set_permissions(&executable, fs::Permissions::from_mode(0o700)).unwrap();
json_output(&run(
home,
&[
"harness",
"add",
executable.to_str().unwrap(),
"--workspace",
root.join("scratch").to_str().unwrap(),
"--prompt",
"scratch",
],
&[],
));
}
#[test]
fn foreground_admission_lock_is_released_before_slow_harness_execution() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("brgr");
let repository = temp.path().join("repo");
fs::create_dir_all(&repository).unwrap();
seed_git_repo(&repository);
add_fixture(&home, &gjc_fixture(), &temp.path().join("gjc-scratch"));
add_slow_prompt_file_harness(&home, &temp.path().join("slow"));
let repo = repository.to_str().unwrap().to_owned();
let home_path = home.clone();
thread::scope(|scope| {
let first = scope.spawn(|| {
run(
&home_path,
&[
"run",
"SLOW_RUN",
"--harness",
"local.slow-agent",
"--workspace",
&repo,
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:worktree-slow")],
)
});
let admitted = Instant::now();
while admitted.elapsed() < Duration::from_secs(2) {
if home_path
.join("launches")
.read_dir()
.map_or(0, Iterator::count)
> 0
{
break;
}
thread::sleep(Duration::from_millis(25));
}
let second_started = Instant::now();
let second = json_output(&run(
&home_path,
&[
"run",
"BRGR_FIXTURE_OK",
"--harness",
"local.gjc",
"--workspace",
&repo,
"--foreground",
],
&[("BRGR_OWNER_ID", "codex:worktree-fast")],
));
let second_elapsed = second_started.elapsed();
assert_eq!(second["outcome"], "candidate");
assert!(
second_elapsed < Duration::from_secs(3),
"second admission waited on the first foreground run: {second_elapsed:?}"
);
assert!(
!first.is_finished(),
"second task finished only after the slow first harness completed"
);
let first = json_output(&first.join().unwrap());
assert_eq!(first["outcome"], "candidate");
assert_ne!(first["task_id"], second["task_id"]);
assert_ne!(first["result_id"], second["result_id"]);
});
assert!(git_branches(&repository).contains("user/keep-me"));
assert_eq!(
fs::read_to_string(repository.join("README")).unwrap(),
"seed\n"
);
}