@@ -1301,21 +1301,33 @@ fn detect_auto_review_phase(progress: Option<&str>) -> AutoReviewPhase {
13011301const SKIP_REVIEW_PROGRESS_SENTINEL: &str = "Another review is already running; skipping this /review.";
13021302const AUTO_REVIEW_SHARED_WORKTREE: &str = "auto-review";
13031303const AUTO_REVIEW_FALLBACK_PREFIX: &str = "auto-review-";
1304+ const AUTO_REVIEW_BASELINE_FILENAME: &str = "auto-review-baseline";
13041305const AUTO_REVIEW_FALLBACK_MAX: usize = 3;
13051306const AUTO_REVIEW_FALLBACK_MAX_AGE_SECS: u64 = 12 * 60 * 60; // 12h
13061307const AUTO_REVIEW_STALE_SECS: u64 = 5 * 60;
13071308
1308- fn auto_review_branches_dir (git_root: &Path) -> Result<PathBuf, String> {
1309+ fn auto_review_repo_dir (git_root: &Path) -> Result<PathBuf, String> {
13091310 let repo_name = git_root
13101311 .file_name()
13111312 .and_then(|s| s.to_str())
13121313 .unwrap_or("repo");
1313- let mut code_home = code_core::config::find_code_home()
1314+ let code_home = code_core::config::find_code_home()
13141315 .map_err(|e| format!("failed to locate code home: {e}"))?;
1315- code_home = code_home.join("working").join(repo_name).join("branches");
1316- std::fs::create_dir_all(&code_home)
1316+ let repo_dir = code_home.join("working").join(repo_name);
1317+ std::fs::create_dir_all(&repo_dir)
1318+ .map_err(|e| format!("failed to create auto review repo dir: {e}"))?;
1319+ Ok(repo_dir)
1320+ }
1321+
1322+ fn auto_review_branches_dir(git_root: &Path) -> Result<PathBuf, String> {
1323+ let branches_dir = auto_review_repo_dir(git_root)?.join("branches");
1324+ std::fs::create_dir_all(&branches_dir)
13171325 .map_err(|e| format!("failed to create branches dir: {e}"))?;
1318- Ok(code_home)
1326+ Ok(branches_dir)
1327+ }
1328+
1329+ fn auto_review_baseline_path_for_repo(git_root: &Path) -> Result<PathBuf, String> {
1330+ Ok(auto_review_repo_dir(git_root)?.join(AUTO_REVIEW_BASELINE_FILENAME))
13191331}
13201332
13211333fn resolve_auto_review_worktree_path(git_root: &Path, branch: &str) -> Option<PathBuf> {
@@ -6626,6 +6638,7 @@ impl ChatWidget<'_> {
66266638 resume_placeholder_visible: false,
66276639 resume_picker_loading: false,
66286640 };
6641+ new_widget.load_auto_review_baseline_marker();
66296642 new_widget.spawn_conversation_runtime(config.clone(), auth_manager.clone(), code_op_rx);
66306643 if let Ok(Some(active_id)) = auth_accounts::get_active_account_id(&config.code_home) {
66316644 if let Ok(records) = account_usage::list_rate_limit_snapshots(&config.code_home) {
@@ -6997,6 +7010,7 @@ impl ChatWidget<'_> {
69977010 resume_placeholder_visible: false,
69987011 resume_picker_loading: false,
69997012 };
7013+ w.load_auto_review_baseline_marker();
70007014 if let Ok(Some(active_id)) = auth_accounts::get_active_account_id(&config.code_home) {
70017015 if let Ok(records) = account_usage::list_rate_limit_snapshots(&config.code_home) {
70027016 if let Some(record) = records.into_iter().find(|r| r.account_id == active_id) {
@@ -33432,6 +33446,66 @@ impl ChatWidget<'_> {
3343233446 self.request_redraw();
3343333447 }
3343433448
33449+ fn auto_review_git_root(&self) -> Option<PathBuf> {
33450+ self.run_git_command(["rev-parse", "--show-toplevel"], |stdout| {
33451+ let root = stdout.lines().next().unwrap_or("").trim();
33452+ if root.is_empty() {
33453+ Err("auto review git root unavailable".to_string())
33454+ } else {
33455+ Ok(root.to_string())
33456+ }
33457+ })
33458+ .ok()
33459+ .map(PathBuf::from)
33460+ }
33461+
33462+ fn auto_review_baseline_path(&self) -> Option<PathBuf> {
33463+ let git_root = self.auto_review_git_root()?;
33464+ match auto_review_baseline_path_for_repo(&git_root) {
33465+ Ok(path) => Some(path),
33466+ Err(err) => {
33467+ tracing::warn!("failed to resolve auto review baseline path: {err}");
33468+ None
33469+ }
33470+ }
33471+ }
33472+
33473+ fn load_auto_review_baseline_marker(&mut self) {
33474+ if self.auto_review_reviewed_marker.is_some() {
33475+ return;
33476+ }
33477+ let Some(path) = self.auto_review_baseline_path() else {
33478+ return;
33479+ };
33480+ let Ok(contents) = std::fs::read_to_string(&path) else {
33481+ return;
33482+ };
33483+ let commit_id = contents.lines().next().unwrap_or("").trim();
33484+ if commit_id.is_empty() {
33485+ return;
33486+ }
33487+ self.auto_review_reviewed_marker = Some(GhostCommit::new(commit_id.to_string(), None));
33488+ }
33489+
33490+ fn persist_auto_review_baseline_marker(&self, commit_id: &str) {
33491+ let commit_id = commit_id.trim();
33492+ if commit_id.is_empty() {
33493+ return;
33494+ }
33495+ let Some(path) = self.auto_review_baseline_path() else {
33496+ return;
33497+ };
33498+ if let Some(parent) = path.parent() {
33499+ if let Err(err) = std::fs::create_dir_all(parent) {
33500+ tracing::warn!("failed to create auto review baseline dir: {err}");
33501+ return;
33502+ }
33503+ }
33504+ if let Err(err) = std::fs::write(&path, format!("{commit_id}\n")) {
33505+ tracing::warn!("failed to persist auto review baseline: {err}");
33506+ }
33507+ }
33508+
3343533509 fn maybe_trigger_auto_review(&mut self) {
3343633510 if !self.config.tui.auto_review_enabled {
3343733511 return;
@@ -34146,6 +34220,7 @@ impl ChatWidget<'_> {
3414634220 if !errored {
3414734221 if let Some(id) = snapshot.as_ref() {
3414834222 self.auto_review_reviewed_marker = Some(GhostCommit::new(id.clone(), None));
34223+ self.persist_auto_review_baseline_marker(id);
3414934224 }
3415034225 }
3415134226
0 commit comments