diff --git a/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs b/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs index 2f371e6..42d31b4 100644 --- a/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs +++ b/OpenNest.FrontEnd.Tests/McpMixReportingTests.cs @@ -37,7 +37,8 @@ public class McpMixReportingTests var text = new NestingTools(session).PackPlate(0, "A,B", "10,10"); Assert.Contains("A: requested=10", text); Assert.Contains("B: requested=10, placed=0, remaining=10", text); - Assert.Contains("ratio not met", text); + Assert.Contains("remaining demand", text); + Assert.DoesNotContain("ratio not met", text); Assert.Contains($"A: requested=10, placed={plate.Parts.Count - 1}, remaining={11 - plate.Parts.Count}", text); Assert.True(plate.Parts.Count > 1); } @@ -52,7 +53,8 @@ public class McpMixReportingTests var text = tool.AutoNestPlate(0, "A,B", "2,2", engine); Assert.Contains("A: requested=2, placed=1, remaining=1", text); Assert.Contains("B: requested=2, placed=0, remaining=2", text); - Assert.Contains("ratio not met", text); + Assert.Contains("remaining demand", text); + Assert.DoesNotContain("ratio not met", text); // Duplicate tokens must be refused before the engine can mutate a fresh target. var fresh = Session(); diff --git a/OpenNest.Mcp/Tools/NestingTools.cs b/OpenNest.Mcp/Tools/NestingTools.cs index 018f6a7..c8f90f8 100644 --- a/OpenNest.Mcp/Tools/NestingTools.cs +++ b/OpenNest.Mcp/Tools/NestingTools.cs @@ -350,21 +350,12 @@ namespace OpenNest.Mcp.Tools sb.AppendLine($" [{i}] {item.Drawing.Name}: requested={item.Quantity}, placed={placed[i]}, remaining={remaining}"); } - var total = placed.Sum(count => (long)count); - if (total == 0) + if (placed.All(count => count == 0)) sb.AppendLine(" Mix: zero progress for all requested drawings."); - else if (items.Count > 1 && items.All(item => item.Quantity > 0)) - { - var balanced = true; - for (var i = 1; i < items.Count; i++) - if ((long)placed[i] * items[0].Quantity != (long)placed[0] * items[i].Quantity) - balanced = false; - sb.AppendLine(balanced - ? " Mix: ratio satisfied for new placements; remaining demand may still exist." - : " Warning: ratio not met by new placements; quantities are ceilings, not a balance constraint."); - } - else if (items.Count > 1) - sb.AppendLine(" Mix: ratio not evaluated for unlimited or nonpositive requested quantities."); + else if (items.Where((item, i) => item.Quantity > placed[i]).Any()) + sb.AppendLine(" Warning: remaining demand in this call; place the outstanding quantities on another sheet before treating the job as complete."); + else + sb.AppendLine(" Mix: requested quantities placed in this call."); } /// diff --git a/docs/automatic-nesting.md b/docs/automatic-nesting.md index e5eb0aa..d274f97 100644 --- a/docs/automatic-nesting.md +++ b/docs/automatic-nesting.md @@ -48,7 +48,7 @@ Invalid output is printed and rejected with exit code 2 without saving or postin MCP `autonest_plate` requires an empty target. The stdio server serializes all tool calls sharing its mutable session, so another request cannot change drawings or occupy a target during a solve. `allow_invalid` defaults to false. It reports violations and makes no changes on rejection, including with an override when the output is unrepresentable or contains multiple sheets. `engine` defaults to Default, and its description lists the built-in engines with what each suits. Fill tools (`fill_plate`, `fill_area`, `fill_remnants`, `pack_plate`) remain separate and default to the Fill strategy. Console and MCP load jobs plug-ins from `Engines/` beside their executable. -MCP `pack_plate` and `autonest_plate` report each requested drawing's newly placed count and remaining ceiling, indexed by request; a skewed positive-demand mix gets an explicit `ratio not met` warning. This is reporting, not proportional packing: both tools still choose the same density-first layout. Duplicate request tokens and ambiguous drawing names are refused before placement. A zero-quantity pack request places zero parts for that drawing; negative quantities are rejected, and neither has a meaningful proportional mix. A partially placed balanced mix can still leave demand unfilled. Strict per-sheet ratio enforcement is not available yet. +MCP `pack_plate` and `autonest_plate` report each requested drawing's newly placed count and remaining quantity, indexed by request; partial fulfillment is explicitly warned about. Per-sheet ratios are not enforced or required: an uneven sheet is acceptable if the remaining quantities are placed elsewhere in the nest. These tools operate on a single selected plate and do not yet automatically finish a multi-sheet job; callers must track and place the outstanding quantities. Duplicate request tokens and ambiguous drawing names are refused before placement. A zero-quantity pack request places zero parts for that drawing; negative quantities are rejected. MCP `delete_plate(plateIndex)` removes only a sheet with no parts and no cutoff definitions, from either loaded or newly created session plates. Occupied, cutoff-bearing and invalid indices are refused without changing the session. After deletion every later index shifts down by one; inspect `get_plate_info` before further edits. This is session cleanup, not a data purge or a save-time option. `save_nest` lists all session plates in its response, but the existing `.nest` writer omits empty cutoff-free plates from the serialized file; reloading may therefore have fewer plates than the response says. There is no opt-in switch to preserve empty plates in the file.