truth-mirror 0.9.1

Truthfulness gate and adversarial reviewer harness for AI coding agents.
Documentation
use assert_cmd::Command;
use predicates::prelude::*;

fn help_text(args: &[&str]) -> String {
    let output = Command::cargo_bin("truth-mirror")
        .unwrap()
        .args(args)
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();

    String::from_utf8(output).unwrap()
}

fn help_text_for_bin(bin: &str, args: &[&str]) -> String {
    let output = Command::cargo_bin(bin)
        .unwrap()
        .args(args)
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();

    String::from_utf8(output).unwrap()
}

#[test]
fn root_help_exposes_brief_defined_surface() {
    let help = help_text(&["--help"]);

    for expected in [
        "install-hooks",
        "review",
        "gate",
        "reinject",
        "ledger",
        "watch",
        "status",
    ] {
        assert!(
            help.contains(expected),
            "missing {expected} in help:\n{help}"
        );
    }
}

#[test]
fn truth_alias_exposes_same_cli_surface() {
    let help = help_text_for_bin("truth", &["--help"]);

    assert!(help.contains("Usage: truth"), "wrong alias help:\n{help}");
    for expected in [
        "install-hooks",
        "review",
        "gate",
        "reinject",
        "ledger",
        "watch",
        "status",
    ] {
        assert!(
            help.contains(expected),
            "missing {expected} in truth help:\n{help}"
        );
    }
}

#[test]
fn truth_alias_reports_own_binary_name_in_version() {
    let version = help_text_for_bin("truth", &["--version"]);

    assert_eq!(
        version.trim(),
        format!("truth {}", env!("CARGO_PKG_VERSION"))
    );
}

#[test]
fn status_help_exposes_repo_diagnostics() {
    let help = help_text(&["status", "--help"]);

    assert!(
        help.contains("hook wiring"),
        "missing status diagnostics description in help:\n{help}"
    );
}

#[test]
fn watch_once_and_until_empty_are_mutually_exclusive() {
    Command::cargo_bin("truth-mirror")
        .unwrap()
        .args(["watch", "--once", "--until-empty"])
        .assert()
        .failure()
        .stderr(predicate::str::contains(
            "the argument '--once' cannot be used with '--until-empty'",
        ));
}

#[test]
fn install_hooks_help_exposes_agent_and_lifecycle_flags() {
    let help = help_text(&["install-hooks", "--help"]);

    for expected in [
        "--claude",
        "--codex",
        "--pi",
        "--grok",
        "--uninstall",
        "--dry-run",
        "--inject-forwarder",
    ] {
        assert!(
            help.contains(expected),
            "missing {expected} in help:\n{help}"
        );
    }
}

#[test]
fn review_help_exposes_model_opposition_and_strict_mode_flags() {
    let help = help_text(&["review", "--help"]);

    for expected in [
        "--staged",
        "--watched-agent",
        "--reviewer-harness",
        "--watched-model",
        "--reviewer-model",
        "--allow-same-model",
        "--strict-two-pass",
        "--scope",
        "--base",
        "status",
        "result",
        "cancel",
    ] {
        assert!(
            help.contains(expected),
            "missing {expected} in help:\n{help}"
        );
    }
}

#[test]
fn gate_help_exposes_pre_push_range() {
    let help = help_text(&["gate", "--help"]);

    assert!(
        help.contains("--pre-push"),
        "missing --pre-push in help:\n{help}"
    );
    assert!(
        help.contains("--commit-msg"),
        "missing --commit-msg in help:\n{help}"
    );
    assert!(
        help.contains("--claim-file"),
        "missing --claim-file in help:\n{help}"
    );
    assert!(
        help.contains("--diff-file"),
        "missing --diff-file in help:\n{help}"
    );
    assert!(
        help.contains("RANGE"),
        "missing RANGE value name in help:\n{help}"
    );
}

#[test]
fn reinject_help_requires_agent_selection() {
    let help = help_text(&["reinject", "--help"]);

    assert!(help.contains("--agent"), "missing --agent in help:\n{help}");
    for expected in ["claude", "codex", "pi", "grok"] {
        assert!(
            help.contains(expected),
            "missing {expected} in help:\n{help}"
        );
    }
}

#[test]
fn ledger_help_exposes_all_required_subcommands() {
    let help = help_text(&["ledger", "--help"]);

    // `resolve` and `waive` moved to the new top-level `resolve` subcommand.
    // `ledger history` is the new audit-trail subcommand.
    for expected in ["list", "show", "history", "stats"] {
        assert!(
            help.contains(expected),
            "missing {expected} in help:\n{help}"
        );
    }
}