use crate::git;
use crate::parse::extract_section;
use crate::ticket::parse_ticket;
pub fn generate_review_brief(slug: &str, trunk: &str, plan_dir: &str) -> Result<String, String> {
let branch = format!("plan/{slug}");
git::rev_parse_verify(&branch).map_err(|_| format!("no such branch: {branch}"))?;
let task_files = git::ls_tree_md(&branch, &format!("{plan_dir}/tasks"))
.map_err(|_| format!("no task file for '{slug}' on {branch}"))?;
let task_pattern = format!(r"/[0-9]+-{}\.md$", regex::escape(slug));
let re = regex::Regex::new(&task_pattern).unwrap();
let task_file = task_files
.iter()
.find(|f| re.is_match(f))
.ok_or_else(|| format!("no task file for '{slug}' on {branch}"))?;
let worktree_path = find_worktree_path(&branch);
let blob = git::show_ref(&branch, task_file)?;
let ticket = parse_ticket(&blob);
let acceptance = extract_section(&ticket.raw, "Acceptance");
let validation_raw = extract_section(&ticket.raw, "Validation");
let validation: Vec<&str> = validation_raw
.lines()
.filter(|l| !l.trim().is_empty())
.collect();
let validation = validation.join("\n");
let diff = git::diff_refs(trunk, &branch)?;
let guidance = "--- reviewer guidance ---\n\
You are an independent reviewer in fresh context. Do NOT trust the worker's\n\
self-validation; re-check everything yourself.\n\
\n\
1. Read ## Acceptance above and the diff.\n\
2. In the worktree, RUN the acceptance checks yourself (tests, commands,\n\
manual verification).\n\
3. Edit ONLY the task file (never code). Add a ## Review section:\n\
## Review\n\
verdict: approved # or: changes-requested\n\
reviewer: <your id>\n\
date: <YYYY-MM-DD>\n\
<what you re-checked and the result>\n\
4. If approved: leave status: review, commit, hand back to the leader.\n\
5. If changes-requested: also flip status: in_progress, record concretely what\n\
failed, commit, hand back. The worker will be re-dispatched.";
let display_wt = worktree_path
.as_deref()
.unwrap_or("(none -- checkout plan/<slug> to review)");
let mut out = String::new();
out.push_str(&format!("branch: {branch}\n"));
out.push_str(&format!("task: {task_file}\n"));
out.push_str(&format!("worktree: {display_wt}\n"));
out.push('\n');
out.push_str("--- acceptance ---\n");
out.push_str(&acceptance);
out.push('\n');
out.push('\n');
out.push_str("--- validation (worker self-check) ---\n");
out.push_str(&validation);
out.push('\n');
out.push('\n');
out.push_str(&format!("--- diff vs {trunk} ---\n"));
out.push_str(&diff);
out.push('\n');
out.push('\n');
out.push_str(guidance);
out.push('\n');
Ok(out)
}
fn find_worktree_path(branch: &str) -> Option<String> {
let lines = git::worktree_list().ok()?;
let branch_ref = format!("refs/heads/{branch}");
let mut current_wt: Option<String> = None;
for line in &lines {
if let Some(path) = line.strip_prefix("worktree ") {
current_wt = Some(path.to_string());
} else if line.strip_prefix("branch ") == Some(&branch_ref) {
return current_wt;
}
}
None
}