From 2f91f1b03659e542fbc4552a9ae648116fcdc8a4 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 16 Oct 2026 15:30:32 -0400 Subject: [PATCH] fix(engine): forged pinned-contour ordinal stays an Incomplete verdict The 5.5 pinned-entry shortcut in ContourEntryFeasibility looked up the choice's contour before the probe's own validation, so an owned choice with an out-of-range contour ordinal threw instead of returning Incomplete as it did before. The lookup now runs only for an ordinal inside the preparation. Co-Authored-By: Claude Opus 5.5 --- OpenNest.Engine/CuttingPlanning/ContourEntryFeasibility.cs | 4 +++- .../CuttingPlanning/ContourEntryFeasibilityTests.cs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/OpenNest.Engine/CuttingPlanning/ContourEntryFeasibility.cs b/OpenNest.Engine/CuttingPlanning/ContourEntryFeasibility.cs index ee88fd48..024782ee 100644 --- a/OpenNest.Engine/CuttingPlanning/ContourEntryFeasibility.cs +++ b/OpenNest.Engine/CuttingPlanning/ContourEntryFeasibility.cs @@ -90,7 +90,9 @@ public sealed class ContourEntryFeasibility return new(ContourFeasibilityStatus.Incomplete, "Foreign contour choice."); // A drawing pin's entry is a fixed emission, accepted as given: it is never filtered // out here. The emitted program's checks report its violations as warnings. - if (prepared.PinnedEntry(choice.ContourOrdinal) == choice) + // A forged ordinal falls through to the probe, which refuses it as Incomplete. + if (choice.ContourOrdinal >= 0 && choice.ContourOrdinal < prepared.Count + && prepared.PinnedEntry(choice.ContourOrdinal) == choice) return new(ContourFeasibilityStatus.Clear, null); var key = new Key(choice.ContourOrdinal, choice.EntityOrdinal, choice.Point.X, choice.Point.Y, nodeContext ?? string.Empty); diff --git a/OpenNest.Tests/CuttingPlanning/ContourEntryFeasibilityTests.cs b/OpenNest.Tests/CuttingPlanning/ContourEntryFeasibilityTests.cs index 9f432a4f..d5a97e01 100644 --- a/OpenNest.Tests/CuttingPlanning/ContourEntryFeasibilityTests.cs +++ b/OpenNest.Tests/CuttingPlanning/ContourEntryFeasibilityTests.cs @@ -176,12 +176,14 @@ public class ContourEntryFeasibilityTests var (other, _, _) = PreparedSquare(); var forgedContour = choice with { Owner = other, ContourOrdinal = 99 }; var forgedEntity = choice with { Owner = prepared, EntityOrdinal = 99 }; + var forgedOwnedContour = choice with { Owner = prepared, ContourOrdinal = 99 }; var feasibility = new ContourEntryFeasibility(prepared, At, own, []); var verdict = feasibility.Check(forgedContour); Assert.Equal(ContourFeasibilityStatus.Incomplete, verdict.Status); Assert.False(string.IsNullOrEmpty(verdict.Reason)); Assert.Equal(ContourFeasibilityStatus.Incomplete, feasibility.Check(forgedEntity).Status); + Assert.Equal(ContourFeasibilityStatus.Incomplete, feasibility.Check(forgedOwnedContour).Status); } [Fact]