mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(tui/auto-review): defer baseline capture (fixes #459)
This commit is contained in:
@@ -3419,6 +3419,11 @@ impl App<'_> {
|
||||
widget.handle_ghost_snapshot_finished(job_id, result, elapsed);
|
||||
}
|
||||
}
|
||||
AppEvent::AutoReviewBaselineCaptured { turn_sequence, result } => {
|
||||
if let AppState::Chat { widget } = &mut self.app_state {
|
||||
widget.handle_auto_review_baseline_captured(turn_sequence, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.alt_screen_active {
|
||||
|
||||
@@ -139,6 +139,12 @@ pub(crate) enum AppEvent {
|
||||
elapsed: Duration,
|
||||
},
|
||||
|
||||
/// Background auto-review baseline capture finished (non-blocking).
|
||||
AutoReviewBaselineCaptured {
|
||||
turn_sequence: u64,
|
||||
result: Result<GhostCommit, GitToolingError>,
|
||||
},
|
||||
|
||||
/// Internal: flush any pending out-of-order ExecEnd events that did not
|
||||
/// receive a matching ExecBegin within a short pairing window. This lets
|
||||
/// the TUI render a fallback "Ran call_<id>" cell so output is not lost.
|
||||
|
||||
@@ -12628,10 +12628,7 @@ impl ChatWidget<'_> {
|
||||
// covers changes made during the turn, not pre-existing local edits.
|
||||
self.auto_review_baseline = None;
|
||||
if self.config.tui.auto_review_enabled {
|
||||
match self.capture_auto_turn_commit("auto review baseline snapshot", None) {
|
||||
Ok(commit) => self.auto_review_baseline = Some(commit),
|
||||
Err(err) => tracing::warn!("failed to capture auto review baseline: {err}"),
|
||||
}
|
||||
self.spawn_auto_review_baseline_capture();
|
||||
}
|
||||
|
||||
// Don't add loading cell - we have progress in the input area
|
||||
@@ -18040,6 +18037,82 @@ Have we met every part of this goal and is there no further work to do?"#
|
||||
result
|
||||
}
|
||||
|
||||
fn capture_auto_review_baseline_for_path(
|
||||
repo_path: PathBuf,
|
||||
) -> Result<GhostCommit, GitToolingError> {
|
||||
#[cfg(test)]
|
||||
if let Some(stub) = CAPTURE_AUTO_TURN_COMMIT_STUB.lock().unwrap().as_ref() {
|
||||
return stub("auto review baseline snapshot", None);
|
||||
}
|
||||
let hook_repo = repo_path.clone();
|
||||
let options =
|
||||
CreateGhostCommitOptions::new(repo_path.as_path()).message("auto review baseline snapshot");
|
||||
let hook = move || bump_snapshot_epoch_for(&hook_repo);
|
||||
let result = create_ghost_commit(&options.post_commit_hook(&hook));
|
||||
if result.is_ok() {
|
||||
bump_snapshot_epoch_for(&repo_path);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn spawn_auto_review_baseline_capture(&mut self) {
|
||||
let turn_sequence = self.turn_sequence;
|
||||
let repo_path = self.config.cwd.clone();
|
||||
let app_event_tx = self.app_event_tx.clone();
|
||||
|
||||
if let Ok(handle) = tokio::runtime::Handle::try_current() {
|
||||
handle.spawn(async move {
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
ChatWidget::capture_auto_review_baseline_for_path(repo_path)
|
||||
})
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
Err(GitToolingError::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("auto review baseline task failed: {err}"),
|
||||
)))
|
||||
});
|
||||
app_event_tx.send(AppEvent::AutoReviewBaselineCaptured {
|
||||
turn_sequence,
|
||||
result,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
std::thread::spawn(move || {
|
||||
let result = ChatWidget::capture_auto_review_baseline_for_path(repo_path);
|
||||
app_event_tx.send(AppEvent::AutoReviewBaselineCaptured {
|
||||
turn_sequence,
|
||||
result,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_auto_review_baseline_captured(
|
||||
&mut self,
|
||||
turn_sequence: u64,
|
||||
result: Result<GhostCommit, GitToolingError>,
|
||||
) {
|
||||
if turn_sequence != self.turn_sequence {
|
||||
tracing::debug!(
|
||||
"ignored auto review baseline for stale turn_sequence={turn_sequence}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if self.auto_review_baseline.is_some() {
|
||||
tracing::debug!("auto review baseline already set; skipping update");
|
||||
return;
|
||||
}
|
||||
match result {
|
||||
Ok(commit) => {
|
||||
self.auto_review_baseline = Some(commit);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("failed to capture auto review baseline: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-helpers"))]
|
||||
#[allow(dead_code)]
|
||||
fn git_diff_name_only_between(
|
||||
@@ -28432,6 +28505,30 @@ use code_core::protocol::OrderMeta;
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_started_defers_auto_review_baseline_capture() {
|
||||
let _stub_lock = AUTO_STUB_LOCK.lock().unwrap();
|
||||
let _rt = enter_test_runtime_guard();
|
||||
let _capture_guard = CaptureCommitStubGuard::install(|_, _| {
|
||||
Ok(GhostCommit::new("baseline".to_string(), None))
|
||||
});
|
||||
let mut harness = ChatWidgetHarness::new();
|
||||
let chat = harness.chat();
|
||||
chat.config.tui.auto_review_enabled = true;
|
||||
|
||||
chat.handle_code_event(Event {
|
||||
id: "turn-1".to_string(),
|
||||
event_seq: 0,
|
||||
msg: EventMsg::TaskStarted,
|
||||
order: None,
|
||||
});
|
||||
|
||||
assert!(
|
||||
chat.auto_review_baseline.is_none(),
|
||||
"baseline capture should not block TaskStarted"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn background_review_completion_resumes_auto_and_posts_summary() {
|
||||
let _rt = enter_test_runtime_guard();
|
||||
|
||||
Reference in New Issue
Block a user