diff --git a/OpenNest.Engine.Tests/Jobs/NestingEngineRegistryTests.cs b/OpenNest.Engine.Tests/Jobs/NestingEngineRegistryTests.cs
new file mode 100644
index 0000000..0766bca
--- /dev/null
+++ b/OpenNest.Engine.Tests/Jobs/NestingEngineRegistryTests.cs
@@ -0,0 +1,49 @@
+using Xunit;
+
+namespace OpenNest.Engine.Tests.Jobs;
+
+public class NestingEngineRegistryTests
+{
+ [Fact]
+ public void BuiltInStrategiesAreRegistered()
+ {
+ var names = NestingEngineRegistry.AvailableEngines.Select(e => e.Name).ToList();
+
+ Assert.Contains("Default", names);
+ Assert.Contains("Strip", names);
+ Assert.Contains("Vertical Remnant", names);
+ Assert.Contains("Horizontal Remnant", names);
+ }
+
+ [Fact]
+ public void EachBuiltInFactoryProducesAWorkingEngine()
+ {
+ foreach (var info in NestingEngineRegistry.AvailableEngines)
+ {
+ var engine = info.Factory();
+ var result = engine.Solve(FiniteStockJobTests.Job(1));
+
+ Assert.Equal(NestJobStatus.Complete, result.Status);
+ }
+ }
+
+ [Fact]
+ public void DuplicateNameIsSkipped()
+ {
+ var before = NestingEngineRegistry.AvailableEngines.Count;
+
+ NestingEngineRegistry.Register("Default", "duplicate", () => new FixedStrategyNestingEngine("Default"));
+
+ Assert.Equal(before, NestingEngineRegistry.AvailableEngines.Count);
+ }
+
+ [Fact]
+ public void LoadPluginsAgainstMissingDirectoryIsANoOp()
+ {
+ var before = NestingEngineRegistry.AvailableEngines.Count;
+
+ NestingEngineRegistry.LoadPlugins(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
+
+ Assert.Equal(before, NestingEngineRegistry.AvailableEngines.Count);
+ }
+}
diff --git a/OpenNest.Engine/Jobs/NestingEngineInfo.cs b/OpenNest.Engine/Jobs/NestingEngineInfo.cs
new file mode 100644
index 0000000..532893c
--- /dev/null
+++ b/OpenNest.Engine/Jobs/NestingEngineInfo.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace OpenNest;
+
+/// Display metadata plus a fresh-instance factory for one registered whole-job engine.
+public class NestingEngineInfo
+{
+ public NestingEngineInfo(string name, string description, Func factory)
+ {
+ Name = name;
+ Description = description;
+ Factory = factory;
+ }
+
+ public string Name { get; }
+ public string Description { get; }
+ public Func Factory { get; }
+}
diff --git a/OpenNest.Engine/Jobs/NestingEngineRegistry.cs b/OpenNest.Engine/Jobs/NestingEngineRegistry.cs
new file mode 100644
index 0000000..2403c5a
--- /dev/null
+++ b/OpenNest.Engine/Jobs/NestingEngineRegistry.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace OpenNest;
+
+///
+/// Registry of whole-job INestingEngine implementations, parallel to NestEngineRegistry (which is for
+/// the legacy single-plate NestEngineBase). The four production strategies are exposed here through
+/// FixedStrategyNestingEngine so they compete on equal footing with model-submitted engines. Unlike
+/// NestEngineRegistry, this has no ActiveEngineName/global-selection concept — callers choose an engine
+/// explicitly from AvailableEngines.
+///
+public static class NestingEngineRegistry
+{
+ private static readonly List engines = new();
+
+ static NestingEngineRegistry()
+ {
+ Register("Default", "Multi-phase nesting (Linear, Pairs, RectBestFit, Remainder)",
+ () => new FixedStrategyNestingEngine("Default"));
+
+ Register("Strip", "Strip-based nesting for mixed-drawing layouts",
+ () => new FixedStrategyNestingEngine("Strip"));
+
+ Register("Vertical Remnant", "Optimizes for largest right-side vertical drop",
+ () => new FixedStrategyNestingEngine("Vertical Remnant"));
+
+ Register("Horizontal Remnant", "Optimizes for largest top-side horizontal drop",
+ () => new FixedStrategyNestingEngine("Horizontal Remnant"));
+ }
+
+ public static IReadOnlyList AvailableEngines => engines;
+
+ public static void Register(string name, string description, Func factory)
+ {
+ if (engines.Any(e => e.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
+ {
+ Debug.WriteLine($"[NestingEngineRegistry] Duplicate engine '{name}' skipped");
+ return;
+ }
+
+ engines.Add(new NestingEngineInfo(name, description, factory));
+ }
+
+ /// Scans *.dll in directory for non-abstract INestingEngine types with a public
+ /// parameterless constructor, registering each under its CLR type name. Mirrors
+ /// NestEngineRegistry.LoadPlugins's per-assembly/per-type isolation: one bad plugin never
+ /// prevents the rest from loading.
+ public static void LoadPlugins(string directory)
+ {
+ if (!Directory.Exists(directory))
+ return;
+
+ foreach (var dll in Directory.GetFiles(directory, "*.dll"))
+ {
+ try
+ {
+ var assembly = Assembly.LoadFrom(dll);
+
+ foreach (var type in assembly.GetTypes())
+ {
+ if (type.IsAbstract || !typeof(INestingEngine).IsAssignableFrom(type))
+ continue;
+
+ var ctor = type.GetConstructor(Type.EmptyTypes);
+
+ if (ctor == null)
+ {
+ Debug.WriteLine($"[NestingEngineRegistry] Skipping {type.Name}: no parameterless constructor");
+ continue;
+ }
+
+ try
+ {
+ Register(type.Name, string.Empty, () => (INestingEngine)ctor.Invoke(null));
+ Debug.WriteLine($"[NestingEngineRegistry] Loaded plugin engine: {type.Name}");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[NestingEngineRegistry] Failed to register {type.Name}: {ex.Message}");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[NestingEngineRegistry] Failed to load assembly {Path.GetFileName(dll)}: {ex.Message}");
+ }
+ }
+ }
+}