diff --git a/crates/engine/src/jobs.rs b/crates/engine/src/jobs.rs
--- a/crates/engine/src/jobs.rs
+++ b/crates/engine/src/jobs.rs
@@ -106,7 +106,8 @@ impl JobCtx {
     /// Progress never goes backwards: a lower value than already reported is ignored.
     pub fn progress(&self, fraction: f32, message: &str) {
         if fraction.is_finite() {
-            self.shared.progress.fetch_max(fraction.clamp(0.0, 1.0).to_bits(), Ordering::Relaxed);
+            // `+ 0.0` turns -0.0 into +0.0: its bits would otherwise outrank every positive float.
+            self.shared.progress.fetch_max((fraction.clamp(0.0, 1.0) + 0.0).to_bits(), Ordering::Relaxed);
         }
         if !message.is_empty() {
             let mut m = self.shared.message.lock().unwrap_or_else(PoisonError::into_inner);
diff --git a/crates/engine/tests/jobs_progress.rs b/crates/engine/tests/jobs_progress.rs
new file mode 100644
--- /dev/null
+++ b/crates/engine/tests/jobs_progress.rs
@@ -0,0 +1,9 @@
+use photocraft_engine::jobs::JobCtx;
+
+#[test]
+fn negative_zero_progress_does_not_pin_fraction() {
+    let c = JobCtx::new();
+    c.progress(-0.0, "");
+    c.progress(0.5, "");
+    assert_eq!(c.fraction(), 0.5);
+}
