refactor(engine): route console and MCP nesting through named job engines

Console --engine now names a jobs engine for --autonest (solved once
through NestingEngineRegistry.Create and committed onto the plate) or a
built-in fill strategy for single-plate fill through the public
PlateFillService; unknown names exit with the valid choices instead of
consulting the process-global legacy registry. MCP nesting tools take an
explicit engine argument per call with the session default, never read
process-global active-engine state, and reject whole-job engine names on
single-plate fill tools. NestingEngineRegistry gains an explicit Create
(name) resolution; PlateFillService gains a public ResolveStrategy and a
plate-number Nest overload used by interactive callers.
This commit is contained in:
aj
2026-09-22 00:30:03 -04:00
parent f36e124039
commit 88966d118c
8 changed files with 416 additions and 73 deletions
@@ -102,6 +102,33 @@ public class PlateFillServiceTests
Assert.All(parts, part => Assert.Same(drawing, part.BaseDrawing));
}
[Fact]
public void Nest_RejectsUnknownStrategy_AndReportsPlateNumberInDetail()
{
var plate = new Plate(new Size(60, 80));
// Big enough (240 area x4 >= 10% of the 4800-area plate) to route through the fill
// pipeline, which is where progress detail is emitted.
var drawing = new Drawing("part", TestDrawingFactory.Rectangle(20, 12));
var items = new List<NestItem> { new() { Drawing = drawing, Quantity = 4 } };
Assert.Throws<NotSupportedException>(() =>
PlateFillService.Nest("StockLadder", plate, items, null, CancellationToken.None)
);
Assert.Throws<ArgumentNullException>(() =>
PlateFillService.Nest(null!, plate, items, null, CancellationToken.None)
);
// The plateNumber overload reports the caller's plate index like the legacy
// engine's PlateNumber did for interactive multi-plate loops.
var progress = new CapturingProgress();
var parts = PlateFillService.Nest("Default", plate, items, 3, progress, CancellationToken.None);
Assert.NotEmpty(parts);
Assert.NotEmpty(progress.Reports);
Assert.All(progress.Reports, report => Assert.Equal(3, report.PlateNumber));
Assert.Empty(plate.Parts); // proposed parts only; committing stays with the caller
}
[Theory]
[MemberData(nameof(StrategiesData))]
public void FillItem_ReturnsNoParts_WhenTokenIsAlreadyCancelled(string strategy)