fix: require idle workers for submit
This commit is contained in:
+37
-7
@@ -759,6 +759,12 @@ impl App {
|
||||
}
|
||||
return None;
|
||||
}
|
||||
if self.worker_status != WorkerStatus::Idle {
|
||||
self.push_error(
|
||||
"Submit requires an idle Worker; use Alt-n Notify for work already in progress.",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
self.record_input_history(segments.clone());
|
||||
self.input.clear();
|
||||
Some(self.method_for_run(segments))
|
||||
@@ -773,7 +779,9 @@ impl App {
|
||||
.iter()
|
||||
.any(|segment| matches!(segment, Segment::UploadedFile { .. }))
|
||||
{
|
||||
self.push_error("Notify accepts text only; remove attachments or queue a Submit.");
|
||||
self.push_error(
|
||||
"Notify accepts text only; remove attachments or wait until the Worker is idle to Submit.",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let message = Segment::flatten_to_text(&segments);
|
||||
@@ -3514,16 +3522,22 @@ mod completion_flow_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn running_submit_is_sent_to_the_worker_and_not_queued_locally() {
|
||||
fn running_submit_is_rejected_locally_and_preserves_the_draft() {
|
||||
let mut app = App::new("test".into());
|
||||
app.set_worker_status(WorkerStatus::Running);
|
||||
insert_text(&mut app, "queued turn");
|
||||
insert_text(&mut app, "advisory context");
|
||||
|
||||
let method = app.submit_input();
|
||||
|
||||
assert!(matches!(method, Some(Method::Submit { .. })));
|
||||
assert!(method.is_none());
|
||||
assert_eq!(app.queued_input_count(), 0);
|
||||
assert_eq!(input_text(&app), "");
|
||||
assert_eq!(input_text(&app), "advisory context");
|
||||
assert!(app.blocks.iter().any(|block| matches!(
|
||||
block,
|
||||
Block::Alert { message, .. }
|
||||
if message.contains("requires an idle Worker")
|
||||
&& message.contains("Alt-n Notify")
|
||||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3554,6 +3568,17 @@ mod completion_flow_tests {
|
||||
assert_eq!(app.queued_input_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paused_non_empty_submit_is_rejected_locally_and_preserves_the_draft() {
|
||||
let mut app = App::new("test".into());
|
||||
app.set_worker_status(WorkerStatus::Paused);
|
||||
insert_text(&mut app, "different request");
|
||||
|
||||
assert!(app.submit_input().is_none());
|
||||
assert_eq!(input_text(&app), "different request");
|
||||
assert_eq!(app.queued_input_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paused_empty_submit_still_resumes_immediately() {
|
||||
let mut app = App::new("test".into());
|
||||
@@ -4609,13 +4634,18 @@ mod completion_flow_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_history_records_running_submits_and_suppresses_consecutive_duplicates() {
|
||||
fn input_history_records_only_an_idle_submit_after_running_rejection() {
|
||||
let mut app = App::new("test".into());
|
||||
app.running = true;
|
||||
app.set_worker_status(WorkerStatus::Running);
|
||||
|
||||
for c in "repeat".chars() {
|
||||
app.insert_char(c);
|
||||
}
|
||||
assert!(app.submit_input().is_none());
|
||||
assert_eq!(app.input_history_len(), 0);
|
||||
assert_eq!(input_text(&app), "repeat");
|
||||
|
||||
app.set_worker_status(WorkerStatus::Idle);
|
||||
assert!(app.submit_input().is_some());
|
||||
assert_eq!(app.input_history_len(), 1);
|
||||
assert_eq!(app.queued_input_count(), 0);
|
||||
|
||||
@@ -1682,8 +1682,8 @@ mod tests {
|
||||
assert!(connection.awaiting_attachment_acceptance.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn running_attachment_submit_failure_restores_draft_without_exiting_console() {
|
||||
#[test]
|
||||
fn running_attachment_submit_is_rejected_without_consuming_the_draft() {
|
||||
let file = UploadedFileRef {
|
||||
artifact_id: "artifact-queued".into(),
|
||||
file_name: "queued.txt".into(),
|
||||
@@ -1694,7 +1694,7 @@ mod tests {
|
||||
sha256: "a".repeat(64),
|
||||
source_entry_id: None,
|
||||
};
|
||||
let mut connection = ConsoleConnection {
|
||||
let connection = ConsoleConnection {
|
||||
client: Client::new(FailOnceSocket {
|
||||
fail_next_send: true,
|
||||
}),
|
||||
@@ -1709,13 +1709,10 @@ mod tests {
|
||||
let mut app = App::new("worker".into());
|
||||
app.set_worker_status(WorkerStatus::Running);
|
||||
app.input.insert_str("queued inspect");
|
||||
let method = app
|
||||
.submit_input()
|
||||
.expect("running Submit is sent immediately");
|
||||
|
||||
send_console_method(&mut app, &mut connection, &method)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
app.submit_input().is_none(),
|
||||
"running Submit must be rejected before transport"
|
||||
);
|
||||
|
||||
assert_eq!(app.input.plain_text(), "queued inspect");
|
||||
assert_eq!(connection.pending_attachments, vec![file]);
|
||||
@@ -2035,7 +2032,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn running_enter_sends_submit_to_worker() {
|
||||
fn running_enter_rejects_submit_and_preserves_the_draft() {
|
||||
let mut app = App::new("agent".to_string());
|
||||
app.set_worker_status(WorkerStatus::Running);
|
||||
for c in "queued".chars() {
|
||||
@@ -2048,13 +2045,10 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
assert!(matches!(
|
||||
handle_key(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)),
|
||||
Some(Method::Submit { .. })
|
||||
));
|
||||
assert!(handle_key(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)).is_none());
|
||||
|
||||
assert_eq!(app.queued_input_count(), 0);
|
||||
assert_eq!(input_text(&app), "");
|
||||
assert_eq!(input_text(&app), "queued");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1868,7 +1868,7 @@ fn draw_status(frame: &mut Frame, app: &App, area: Rect) {
|
||||
.add_modifier(Modifier::BOLD),
|
||||
));
|
||||
spans.push(Span::styled(
|
||||
" — Enter to resume, Ctrl-X to cancel, type to start new turn",
|
||||
" — Enter to resume, Ctrl-X to cancel, Alt-n to notify",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
));
|
||||
} else if !app.running {
|
||||
|
||||
Reference in New Issue
Block a user