fix(engine): isolate job identity and engine selection

Replace name-based quantity deduction with reference-based drawing
identity in the engine paths the whole-job runner reaches
(NestEngineBase fill/pack, StripNestEngine deduction, RemnantFiller
ledger, IterativeShrinkFiller leftovers). Add instance-scoped
PlateNesterFactory that resolves built-in strategies without touching
the global NestEngineRegistry. Add identity and engine-selection tests.

44 net8.0 tests pass in Debug and Release; no new warnings.
This commit is contained in:
aj
2026-09-17 15:35:59 -04:00
parent 0963b051be
commit 5b88d85937
8 changed files with 279 additions and 22 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ The new whole-job contracts in `OpenNest.Engine/Jobs` (`namespace OpenNest`) use
**Current scope (minimum runnable slice, not release-ready):** `NestJobRunner.Solve` allocates one stock entry across multiple physical sheets, respecting finite inventory (`null` is unlimited), positive remaining demand, and `MaxPlates`. Empty parts complete without consuming stock; empty/unavailable stock returns `Incomplete/StockExhausted`. A zero-placement candidate stops with `NoPlacementFound` and consumes no sheet. Nonempty jobs with multiple stock entries explicitly throw `NotSupportedException`; mixed-stock selection and optional optimizations are not implemented.
`DrawingJobMapper` snapshots caller drawings/items under explicit requirement IDs. `LegacyPlateNesterAdapter` creates fresh private legacy drawings, items, and plates for each trial and maps returned drawings **by reference**, never by name. Mutable legacy quantities never drive the fulfillment ledger. The minimal built-in factory accepts only `Default`; injected factories must explicitly reject unknown keys or return null. `NestResultMaterializer` returns a detached domain nest and `DrawingsByPartId` identity map. Each output plate represents one physical sheet (`Quantity = 1`), and each placement is attached exactly once so domain quantity events do not double count.
`DrawingJobMapper` snapshots caller drawings/items under explicit requirement IDs. `LegacyPlateNesterAdapter` creates fresh private legacy drawings, items, and plates for each trial and maps returned drawings **by reference**, never by name. Mutable legacy quantities never drive the fulfillment ledger. `PlateNesterFactory` resolves the built-in strategies (`Default`, `Strip`, `Vertical Remnant`, `Horizontal Remnant`) to private engine factories; it neither reads nor changes the process-global `NestEngineRegistry`, and unknown keys reject. Quantity deduction in the engine paths the runner reaches (base-class fill/pack, strip deduction, remnant-fill ledger, shrink-leftover counting) is keyed by drawing reference, not display name, so same-name drawings and repeated requirements stay independent. `NestResultMaterializer` returns a detached domain nest and `DrawingsByPartId` identity map. Each output plate represents one physical sheet (`Quantity = 1`), and each placement is attached exactly once so domain quantity events do not double count.
```csharp
var job = new NestJob(