use std::{path::Path, process::ExitCode};
use anyhow::Result;
use crate::{
cli::{Agent, ReinjectArgs},
ledger::{LedgerEntry, LedgerStore},
};
pub fn run(args: ReinjectArgs, state_dir: &Path) -> Result<ExitCode> {
let entries = LedgerStore::new(state_dir).unresolved_rejections()?;
let output = render(args.agent, &entries);
if !output.is_empty() {
print!("{output}");
}
Ok(ExitCode::SUCCESS)
}
pub fn render(agent: Agent, entries: &[LedgerEntry]) -> String {
if entries.is_empty() {
return String::new();
}
let agent_name = match agent {
Agent::Claude => "Claude",
Agent::Codex => "Codex",
Agent::Pi => "Pi",
};
let mut output = format!(
"Truth Mirror unresolved rejections for {agent_name}. Address these before claiming completion or pushing.\n\n"
);
for entry in entries {
output.push_str(&format!(
"- {} [{}]: {}\n",
entry.commit_sha, entry.disposition, entry.claim
));
for finding in &entry.findings {
output.push_str(&format!(" - {finding}\n"));
}
}
output
}
#[cfg(test)]
mod tests {
use crate::{
cli::Agent,
ledger::{LedgerEntry, ReviewerConfig, Verdict},
};
use super::render;
fn rejected_entry() -> LedgerEntry {
LedgerEntry::new_at(
"abc123",
Verdict::Reject,
"CLAIM: bad | verified: cargo test | evidence: tests:cargo-test",
vec!["tests:cargo-test".to_owned()],
ReviewerConfig::new("claude", "opus", false),
vec!["unsupported".to_owned()],
100,
)
}
#[test]
fn reinjection_is_quiet_when_empty() {
assert_eq!(render(Agent::Claude, &[]), "");
}
#[test]
fn reinjection_mentions_each_supported_agent() {
for agent in [Agent::Claude, Agent::Codex, Agent::Pi] {
let output = render(agent, &[rejected_entry()]);
assert!(output.contains("Truth Mirror unresolved rejections"));
assert!(output.contains("abc123"));
assert!(output.contains("unsupported"));
}
}
}