Re-add exit code check as a guard before consulting RunFinished
A non-zero exit means quire-ci crashed regardless of what events say.
Exit 0 + RunFinished present → use the event outcome. Exit 0 +
RunFinished absent → also treated as a crash (quire-ci reached the end
but never emitted the terminal event).

https://claude.ai/code/session_0135zw5K7qsn9thYkkpjX6HE
change
commit 0846ce514f45df37e5dd0854248668a5fe4d188f
author Claude <noreply@anthropic.com>
date
parent 86d2cfb9
diff --git a/quire-server/src/ci/run.rs b/quire-server/src/ci/run.rs
index a683c1f..6c46f52 100644
--- a/quire-server/src/ci/run.rs
+++ b/quire-server/src/ci/run.rs
@@ -412,9 +412,16 @@ impl Run {
             }
         };
 
-        // RunFinished is the sole signal for outcome: present means quire-ci
-        // reached the end cleanly; absent means it crashed or was killed.
-        // The exit code is kept in the error for diagnostics only.
+        if !status.success() {
+            self.transition(RunState::Failed, Some("process-crashed"))?;
+            return Err(Error::ProcessFailed {
+                exit: status.code(),
+            });
+        }
+
+        // Exit 0: RunFinished determines the pipeline outcome. Absent means
+        // quire-ci exited cleanly but never reached the terminal event —
+        // treat that as a crash too.
         match run_outcome {
             Some(quire_core::ci::event::RunOutcome::Success) => {
                 self.transition(RunState::Complete, None)?;