tui: move eval capture send consent earlier

This commit is contained in:
Noah MacCallum
2026-01-01 15:53:23 -08:00
parent 8f25853583
commit e639300f79
7 changed files with 45 additions and 52 deletions
+3 -6
View File
@@ -815,6 +815,9 @@ impl App {
AppEvent::EvalCaptureIntroContinue => {
self.chat_widget.on_eval_capture_intro_continue();
}
AppEvent::EvalCaptureSendConsent { upload } => {
self.chat_widget.on_eval_capture_send_consent(upload);
}
AppEvent::OpenEvalCaptureNotes { start_marker } => {
self.chat_widget.open_eval_capture_notes(start_marker);
}
@@ -829,12 +832,6 @@ impl App {
what_good_looks_like,
);
}
AppEvent::EvalCaptureUpload { case_id, path } => {
self.chat_widget.upload_eval_capture_bundle(case_id, path);
}
AppEvent::EvalCaptureUploadSkipped { case_id, path } => {
self.chat_widget.eval_capture_upload_skipped(case_id, path);
}
AppEvent::LaunchExternalEditor => {
if self.chat_widget.external_editor_state() == ExternalEditorState::Active {
self.launch_external_editor(tui).await;
+6 -12
View File
@@ -191,6 +191,12 @@ pub(crate) enum AppEvent {
/// Dismiss the eval capture intro screen and continue to the start picker.
EvalCaptureIntroContinue,
/// Record whether the user consented to upload the eval sample bundle once captured.
/// This happens before the start message picker so the full flow is consented up front.
EvalCaptureSendConsent {
upload: bool,
},
/// Open the freeform notes entry for eval capture.
OpenEvalCaptureNotes {
start_marker: EvalCaptureStartMarker,
@@ -203,18 +209,6 @@ pub(crate) enum AppEvent {
what_good_looks_like: String,
},
/// Upload the previously captured eval case bundle to the team.
EvalCaptureUpload {
case_id: String,
path: String,
},
/// Skip uploading the previously captured eval case bundle.
EvalCaptureUploadSkipped {
case_id: String,
path: String,
},
/// Launch the external editor after a normal draw has completed.
LaunchExternalEditor,
}
@@ -83,37 +83,25 @@ pub(crate) fn eval_capture_intro_params(
}
}
pub(crate) fn eval_capture_upload_consent_params(
pub(crate) fn eval_capture_send_consent_params(
app_event_tx: AppEventSender,
case_id: String,
path: String,
) -> super::SelectionViewParams {
let upload_action: super::SelectionAction = Box::new({
let tx = app_event_tx.clone();
let case_id = case_id.clone();
let path = path.clone();
move |_sender: &AppEventSender| {
tx.send(AppEvent::EvalCaptureUpload {
case_id: case_id.clone(),
path: path.clone(),
});
tx.send(AppEvent::EvalCaptureSendConsent { upload: true });
}
});
let skip_action: super::SelectionAction = Box::new({
let tx = app_event_tx;
let case_id = case_id.clone();
let path = path.clone();
move |_sender: &AppEventSender| {
tx.send(AppEvent::EvalCaptureUploadSkipped {
case_id: case_id.clone(),
path: path.clone(),
});
tx.send(AppEvent::EvalCaptureSendConsent { upload: false });
}
});
let header_lines: Vec<Box<dyn crate::render::renderable::Renderable>> = vec![
Line::from("Upload eval sample?".bold()).into(),
Line::from("Send eval sample to the codex team?".bold()).into(),
Line::from("").into(),
Line::from("If you choose Yes, it will upload the full bundle".dim()).into(),
Line::from("to the team:".dim()).into(),
@@ -360,7 +348,7 @@ impl Renderable for EvalCaptureNotesView {
width: area.width,
height: 1,
};
Paragraph::new(Line::from("Enter to continue • Esc to cancel".dim()))
Paragraph::new(Line::from("Enter to continue • Esc to go back".dim()))
.render(hint_area, buf);
}
}
@@ -371,7 +359,9 @@ impl EvalCaptureNotesView {
let subtitle = "Please be specific, and can refer to any aspect of model behavior, including tool calls, code changes, or messages.";
let usable_width = width.saturating_sub(2).max(1) as usize;
let mut lines = Vec::new();
lines.push(Line::from(title.bold()));
for line in textwrap::wrap(title, usable_width) {
lines.push(Line::from(line.into_owned().bold()));
}
for line in textwrap::wrap(subtitle, usable_width) {
lines.push(Line::from(line.into_owned().dim()));
}
+1 -1
View File
@@ -37,8 +37,8 @@ pub(crate) use list_selection_view::SelectionViewParams;
mod feedback_view;
pub(crate) use eval_capture_view::EvalCaptureNotesView;
pub(crate) use eval_capture_view::eval_capture_intro_params;
pub(crate) use eval_capture_view::eval_capture_send_consent_params;
pub(crate) use eval_capture_view::eval_capture_start_picker_params;
pub(crate) use eval_capture_view::eval_capture_upload_consent_params;
pub(crate) use feedback_view::feedback_bad_result_fork_params;
pub(crate) use feedback_view::feedback_selection_params;
pub(crate) use feedback_view::feedback_upload_consent_params;
+24 -8
View File
@@ -370,6 +370,9 @@ pub(crate) struct ChatWidget {
feedback: codex_feedback::CodexFeedback,
// Current session rollout path (if known)
current_rollout_path: Option<PathBuf>,
// Whether the user consented to upload the captured eval sample to the team.
// This is captured before the start picker so the flow is consented up front.
eval_capture_send_to_team: Option<bool>,
external_editor_state: ExternalEditorState,
}
@@ -518,8 +521,9 @@ impl ChatWidget {
}
pub(crate) fn open_eval_capture_intro(&mut self) {
self.eval_capture_send_to_team = None;
if !crate::eval_capture_state::should_show_eval_capture_intro(&self.config.codex_home) {
self.open_eval_capture_start_picker();
self.open_eval_capture_send_consent();
return;
}
@@ -535,6 +539,18 @@ impl ChatWidget {
self.add_error_message(format!("Failed to persist eval capture preference: {err}"));
}
self.open_eval_capture_send_consent();
}
fn open_eval_capture_send_consent(&mut self) {
let params =
crate::bottom_pane::eval_capture_send_consent_params(self.app_event_tx.clone());
self.bottom_pane.show_selection_view(params);
self.request_redraw();
}
pub(crate) fn on_eval_capture_send_consent(&mut self, upload: bool) {
self.eval_capture_send_to_team = Some(upload);
self.open_eval_capture_start_picker();
}
@@ -838,13 +854,11 @@ impl ChatWidget {
crate::history_cell::PlainHistoryCell::new(lines),
)));
let params = crate::bottom_pane::eval_capture_upload_consent_params(
self.app_event_tx.clone(),
case_id,
path,
);
self.bottom_pane.show_selection_view(params);
self.request_redraw();
if self.eval_capture_send_to_team.unwrap_or(false) {
self.upload_eval_capture_bundle(case_id, path);
} else {
self.eval_capture_upload_skipped(case_id, path);
}
}
Err(err) => {
self.app_event_tx
@@ -1949,6 +1963,7 @@ impl ChatWidget {
last_rendered_width: std::cell::Cell::new(None),
feedback,
current_rollout_path: None,
eval_capture_send_to_team: None,
external_editor_state: ExternalEditorState::Closed,
};
@@ -2036,6 +2051,7 @@ impl ChatWidget {
last_rendered_width: std::cell::Cell::new(None),
feedback,
current_rollout_path: None,
eval_capture_send_to_team: None,
external_editor_state: ExternalEditorState::Closed,
};
@@ -1,9 +1,8 @@
---
source: tui/src/chatwidget/tests.rs
assertion_line: 2206
expression: popup
---
Upload eval sample?
Send eval sample to the codex team?
If you choose Yes, it will upload the full bundle
to the team:
+2 -5
View File
@@ -407,6 +407,7 @@ async fn make_chatwidget_manual(
last_rendered_width: std::cell::Cell::new(None),
feedback: codex_feedback::CodexFeedback::new(),
current_rollout_path: None,
eval_capture_send_to_team: None,
external_editor_state: ExternalEditorState::Closed,
};
(widget, rx, op_rx)
@@ -2194,11 +2195,7 @@ async fn eval_capture_intro_popup_snapshot() {
async fn eval_capture_upload_consent_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
let params = crate::bottom_pane::eval_capture_upload_consent_params(
chat.app_event_tx.clone(),
"case-2026-01-01T00-00-00-repo-000001".to_string(),
"/tmp/eval-case/case-2026-01-01T00-00-00-repo-000001".to_string(),
);
let params = crate::bottom_pane::eval_capture_send_consent_params(chat.app_event_tx.clone());
chat.bottom_pane.show_selection_view(params);
chat.request_redraw();