fix: require idle workers for submit

This commit is contained in:
2026-09-23 00:36:54 +09:00
parent d8444e49bd
commit 9857bf04f7
16 changed files with 451 additions and 264 deletions
+14 -12
View File
@@ -1866,18 +1866,20 @@ impl Runtime {
mut input: WorkerInput,
) -> Result<WorkerInteractionAck, RuntimeError> {
validate_worker_input(&input)?;
let expected_submission_id =
if matches!(input.kind, WorkerInputKind::User | WorkerInputKind::Notify) {
let submission_id = input
.submission_request_id
.clone()
.filter(|request_id| !request_id.trim().is_empty())
.unwrap_or_else(|| Uuid::now_v7().to_string());
input.submission_request_id = Some(submission_id.clone());
Some(submission_id)
} else {
None
};
if matches!(input.kind, WorkerInputKind::User | WorkerInputKind::Notify) {
let request_id = input
.submission_request_id
.clone()
.filter(|request_id| !request_id.trim().is_empty())
.unwrap_or_else(|| Uuid::now_v7().to_string());
input.submission_request_id = Some(request_id);
}
let expected_submission_id = (input.kind == WorkerInputKind::User).then(|| {
input
.submission_request_id
.clone()
.expect("User input request id must be assigned before dispatch")
});
self.ensure_worker_execution(worker_ref)?;
let (backend, handle) = {
let state = self.lock()?;
+43 -6
View File
@@ -4360,7 +4360,7 @@ mod tests {
}
#[test]
fn running_worker_accepts_a_second_submit_as_queued() {
fn running_worker_replays_retry_but_rejects_a_new_submit() {
let client = MockClient::sequential(vec![MockResponse::Hang(vec![])]);
let runtime_base = tempfile::tempdir().unwrap();
let cwd = tempfile::tempdir().unwrap();
@@ -4404,13 +4404,50 @@ mod tests {
let mut second_input = WorkerInput::user("second");
second_input.submission_request_id = Some("request-second".into());
let second = runtime
let error = runtime
.send_input(&detail.worker_ref, second_input)
.expect_err("a new Submit must be rejected while the Worker is running");
assert!(error.to_string().contains("requires an idle Worker"));
assert!(error.to_string().contains("use Notify"));
}
#[test]
fn running_worker_accepts_notify_without_a_submit_receipt() {
let client = MockClient::sequential(vec![MockResponse::Hang(vec![])]);
let runtime_base = tempfile::tempdir().unwrap();
let cwd = tempfile::tempdir().unwrap();
let store = tempfile::tempdir().unwrap();
let factory = MockFactory {
client,
runtime_base: runtime_base.path().to_path_buf(),
cwd: cwd.path().to_path_buf(),
store_dir: store.path().join("sessions"),
worker_metadata_dir: store.path().join("workers"),
observed_cwds: Arc::new(Mutex::new(Vec::new())),
observed_workspace_clients: Arc::new(Mutex::new(Vec::new())),
};
let backend = Arc::new(WorkerRuntimeExecutionBackend::new(factory).unwrap());
let runtime =
EmbeddedRuntime::with_execution_backend(RuntimeOptions::default(), backend.clone())
.unwrap();
runtime.store_config_bundle(test_bundle()).unwrap();
let detail = runtime
.create_worker(create_request("running-notify"))
.unwrap();
assert_eq!(
second.submission.as_ref().map(|ack| ack.disposition),
Some(protocol::SubmissionDisposition::Queued)
);
runtime
.send_input(&detail.worker_ref, WorkerInput::user("first"))
.unwrap();
wait_for_adapter_state(&backend, &detail.worker_ref, WorkerStatus::Running);
let mut notification = WorkerInput::notify("advisory context");
notification.submission_request_id = Some("notification-request".into());
let acknowledgement = runtime
.send_input(&detail.worker_ref, notification)
.expect("Running Worker must accept Notify without a Submit receipt");
assert!(acknowledgement.submission.is_none());
wait_for_adapter_state(&backend, &detail.worker_ref, WorkerStatus::Running);
}
#[test]