diff --git a/lib/crates/fabro-cli/tests/it/cmd/attach.rs b/lib/crates/fabro-cli/tests/it/cmd/attach.rs index 16dd2f592..191c4a1bb 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/attach.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/attach.rs @@ -878,6 +878,31 @@ fn attach_json_errors_without_prompting_for_human_input() { }, "run_id": "[ULID]", "ts": "[TIMESTAMP]" + }, + { + "event": "interview.started", + "id": "[EVENT_ID]", + "node_id": "approve", + "node_label": "approve", + "properties": { + "allow_freeform": false, + "options": [ + { + "key": "A", + "label": "[A] Approve" + }, + { + "key": "R", + "label": "[R] Revise" + } + ], + "question": "Approve?", + "question_id": "[ULID]", + "question_type": "multiple_choice", + "stage": "approve" + }, + "run_id": "[ULID]", + "ts": "[TIMESTAMP]" } ] "#); diff --git a/lib/crates/fabro-cli/tests/it/cmd/run.rs b/lib/crates/fabro-cli/tests/it/cmd/run.rs index 1a875ade5..350701a1c 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/run.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/run.rs @@ -955,6 +955,43 @@ fn json_run_implies_auto_approve_for_human_gates() { "run_id": "[ULID]", "ts": "[TIMESTAMP]" }, + { + "event": "interview.started", + "id": "[EVENT_ID]", + "node_id": "approve", + "node_label": "approve", + "properties": { + "allow_freeform": false, + "options": [ + { + "key": "A", + "label": "[A] Approve" + }, + { + "key": "R", + "label": "[R] Revise" + } + ], + "question": "Approve?", + "question_id": "[ULID]", + "question_type": "multiple_choice", + "stage": "approve" + }, + "run_id": "[ULID]", + "ts": "[TIMESTAMP]" + }, + { + "event": "interview.completed", + "id": "[EVENT_ID]", + "properties": { + "answer": "A", + "duration_ms": "[DURATION_MS]", + "question": "Approve?", + "question_id": "[ULID]" + }, + "run_id": "[ULID]", + "ts": "[TIMESTAMP]" + }, { "event": "stage.completed", "id": "[EVENT_ID]", diff --git a/lib/crates/fabro-server/src/server.rs b/lib/crates/fabro-server/src/server.rs index 22802ce93..4d1094bc9 100644 --- a/lib/crates/fabro-server/src/server.rs +++ b/lib/crates/fabro-server/src/server.rs @@ -3796,6 +3796,20 @@ async fn execute_run_subprocess(state: Arc, run_id: RunId) { } } + let superseded = { + let runs = state.runs.lock().expect("runs lock poisoned"); + runs.get(&run_id) + .is_some_and(|managed_run| managed_run.worker_pid != Some(worker_pid)) + }; + if superseded { + tracing::info!( + run_id = %run_id, + worker_pid, + "Skipping stale worker cleanup for superseded run execution" + ); + return; + } + append_worker_exit_failure(&run_store, run_id, &wait_status).await; let final_state = match run_store.state().await { diff --git a/lib/crates/fabro-test/src/lib.rs b/lib/crates/fabro-test/src/lib.rs index c9ee10d3c..4daa990f8 100644 --- a/lib/crates/fabro-test/src/lib.rs +++ b/lib/crates/fabro-test/src/lib.rs @@ -46,6 +46,7 @@ static INSTA_FILTERS: &[(&str, &str)] = &[ const MANAGED_STORAGE_MARKER: &str = "# fabro-test managed storage_dir"; const TEST_IN_MEMORY_STORE_ENV: &str = "FABRO_TEST_IN_MEMORY_STORE"; +const SESSION_LOCK_TIMEOUT: Duration = Duration::from_secs(20); #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum TestMode { @@ -267,7 +268,7 @@ fn with_session_lock(root: &Path, f: impl FnOnce() -> T) -> T { ensure_parent_dir(&lock_path); let lock_file = File::create(&lock_path) .unwrap_or_else(|err| panic!("failed to create {}: {err}", lock_path.display())); - let deadline = std::time::Instant::now() + Duration::from_secs(5); + let deadline = std::time::Instant::now() + SESSION_LOCK_TIMEOUT; while !fabro_proc::try_flock_exclusive(&lock_file) .unwrap_or_else(|err| panic!("failed to lock {}: {err}", lock_path.display())) {