diff --git a/OpenNest.Engine.Tests/Jobs/ClampRemovalRepairTests.cs b/OpenNest.Engine.Tests/Jobs/ClampRemovalRepairTests.cs
new file mode 100644
index 0000000..07b5177
--- /dev/null
+++ b/OpenNest.Engine.Tests/Jobs/ClampRemovalRepairTests.cs
@@ -0,0 +1,87 @@
+using OpenNest.Engine.Jobs;
+using OpenNest.Engine.NestingEngines.Rectangles;
+using OpenNest.Engine.Tests.NestingEngines;
+using OpenNest.Geometry;
+
+namespace OpenNest.Engine.Tests.Jobs;
+
+///
+/// Slice 6 task 6.4: regressions for the independent Slice 3b reviews of last-resort clamp
+/// removal (D8/D9, C10-C13). Geometry: a 23"-long, 3"-wide sheet with 1.5 x 0.625 clamps
+/// centred at X = 6 and X = 21 (clamp-1 spans X [5.25, 6.75], clamp-2 [20.25, 21.75]); a
+/// 3"-tall part always crosses the clamp band, a 14"-wide part fits only with one clamp
+/// removed, a 22"-wide part only with both removed.
+///
+public class ClampRemovalRepairTests
+{
+ private static ClampZone[] Zones() => new[]
+ {
+ new ClampZone("clamp-1", new Box(5.25, 0, 1.5, 0.625)),
+ new ClampZone("clamp-2", new Box(20.25, 0, 1.5, 0.625)),
+ };
+
+ private static NestPlateStock Stock(int? quantity = null) =>
+ JobBuilder.Stock("sheet", width: 3, length: 23, quantity: quantity, clampZones: Zones());
+
+ private static NestItem Item(string name, double w, double h, int quantity) =>
+ new() { Drawing = new Drawing(name, TestDrawingFactory.Rectangle(w, h)), Quantity = quantity };
+
+ private static NestPipelineRequest Request(NestPlateStock stock, params NestItem[] items) =>
+ new("Rectangles", items, new[] { stock });
+
+ /// Delegates the ordinary solve to Rectangles, then fails every later call.
+ private sealed class ProbeThrowEngine : INestingEngine
+ {
+ public int Calls;
+
+ public NestJobResult Solve(NestJob job, IProgress progress = null,
+ CancellationToken token = default)
+ {
+ if (++Calls > 1)
+ throw new InvalidOperationException("feasibility probe failed");
+ return new RectanglesNestingEngine().Solve(job, progress, token);
+ }
+ }
+
+ /// Solves with Rectangles and cancels the run on its second call.
+ private sealed class CancelOnSecondCallEngine(CancellationTokenSource source) : INestingEngine
+ {
+ private int calls;
+
+ public NestJobResult Solve(NestJob job, IProgress progress = null,
+ CancellationToken token = default)
+ {
+ var result = new RectanglesNestingEngine().Solve(job, progress, token);
+ if (++calls == 2)
+ source.Cancel();
+ return result;
+ }
+ }
+
+ // PM 1db4333f: an optional recovery probe failure must not discard the ordinary result.
+ [Fact]
+ public void FailingRecoveryProbeKeepsTheOrdinaryResult()
+ {
+ var engine = new ProbeThrowEngine();
+ var request = Request(Stock(), Item("wide", 14, 3, 1), Item("small", 2, 1, 1));
+
+ var result = NestPipeline.Run(engine, "probe-throw", request);
+
+ Assert.True(engine.Calls > 1, "recovery never probed");
+ Assert.True(result.IsValid, string.Join("; ", result.Violations));
+ // Items map to part ids in request order: part-0 = wide (blocked), part-1 = small.
+ Assert.Equal(1, Assert.Single(result.Raw.Fulfillment, f => f.PartId == "part-0").Unplaced);
+ Assert.Equal(0, Assert.Single(result.Raw.Fulfillment, f => f.PartId == "part-1").Unplaced);
+ Assert.All(result.Job.Plates, s => Assert.DoesNotContain("removed", s.Id));
+ }
+
+ [Fact]
+ public void CancellationDuringRecoveryStillCancelsTheRun()
+ {
+ using var source = new CancellationTokenSource();
+ var request = Request(Stock(), Item("wide", 14, 3, 1), Item("small", 2, 1, 1));
+
+ Assert.ThrowsAny(() =>
+ NestPipeline.Run(new CancelOnSecondCallEngine(source), "cancel", request, token: source.Token));
+ }
+}
diff --git a/OpenNest.Engine/Jobs/ClampRemovalRecovery.cs b/OpenNest.Engine/Jobs/ClampRemovalRecovery.cs
index 2c20831..ed6ddc9 100644
--- a/OpenNest.Engine/Jobs/ClampRemovalRecovery.cs
+++ b/OpenNest.Engine/Jobs/ClampRemovalRecovery.cs
@@ -36,6 +36,22 @@ internal static class ClampRemovalRecovery
ArgumentNullException.ThrowIfNull(raw);
ArgumentNullException.ThrowIfNull(engine);
+ // Recovery is optional: any failure inside it (a throwing probe or residual solve)
+ // keeps the caller's ordinary result instead of discarding it. Cancellation still
+ // propagates.
+ try
+ {
+ return Recover(job, raw, engine, token);
+ }
+ catch (Exception exception) when (exception is not OperationCanceledException)
+ {
+ token.ThrowIfCancellationRequested();
+ return null;
+ }
+ }
+
+ private static Outcome Recover(NestJob job, NestJobResult raw, INestingEngine engine, CancellationToken token)
+ {
if (raw.Status == NestJobStatus.Complete)
return null;