The gap-fill pass stopped on a 120 ms stopwatch and QWEN_* environment
variables switched strategies, so the same job could nest differently
with machine load or environment. Gap fill now stops after eight failed
insertion sweeps, and the switches are internal properties with the
old defaults. Part reading, work area, rotation angles, scoring and
result assembly now use the host APIs; its collision gate is unchanged.
Synthetic benchmark (5 jobs, salvage 0.5): all valid, cost 7660.01 ->
7572.05, time 839 -> 553 ms. Not yet calibrated on production-size jobs.
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opus55 ignored NestJobPart.Priority, so the shared contract test (lower
number wins scarce stock) failed; lower-number priority now precedes its
placement score. SheetEconomics is replaced by the host's NestJobCost so
it optimizes exactly what the benchmark scores, and its footprint margin
comes from NestTolerances.SafeClearanceMargin plus four Clipper grid
units - the same 0.003 total as before, which keeps its contact points.
Synthetic benchmark (5 jobs, salvage 0.5): all valid, cost unchanged at
5452.79, time 611 -> 456 ms.
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gpt6Astra reverse-engineered the validator: hand-tuned paddings and a
copied check sequence (ValidationOverlap) to match its rounding. It now
reads parts with JobPartGeometry, takes clearance from NestTolerances,
checks candidates with NestLayoutCheck.Clears, and assembles results with
NestJobResultBuilder and NestJobCost; its tests use the shared kit. Its
contact search, beam search and extra Automatic angles are unchanged.
Synthetic benchmark (5 jobs, salvage 0.5): all valid, 2 sheets each,
cost 5574.07 -> 5470.07; time 1871 -> 2400 ms from the stricter shared
check on arc-heavy jobs.
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each engine's tests carried its own copy of the job/shape helpers and
validation, and they had drifted: Gpt6Astra linked NestValidator.cs as
source (which stopped compiling once the host moved its checks into
NestLayoutCheck), the others referenced OpenNest.Benchmark. The kit
provides JobBuilder, Shapes, LayoutAssert (backed by the host's public
NestLayoutCheck) and EngineContractTests<TEngine>: quadrants, overflow,
oversize parts, lower-number priority, etch marks, sequential plate
indices, determinism, cancellation and stop reasons, for every engine.
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BuildResult set PlateIndex to the stock index, so every sheet cut from
the same stock shared one index; OpenNest.Api maps plates by it.
Part geometry filtered only rapids, so scribe/etch moves counted as
material - the bug OpenNest fixed in 1b5e1b1. Use SpecialLayers.IsMaterial.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 0.022 margin assumed validators flatten arcs at 0.01; NestValidator
uses 0.001, so a pair can read at most 0.002 closer than true. 0.003
covers that plus the 1e-4 Clipper grid, returning ~0.019 per gap. Arc,
disc, obround and zero-spacing tests still pass.
Part geometry filtered only rapids, so scribe/etch moves counted as
material - the bug OpenNest fixed in 1b5e1b1. Use SpecialLayers.IsMaterial.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Priority was sorted descending, the reverse of the host (StockLadder and
NestJobCandidateComparer treat a lower number as more important), so a
priority-9 part beat a priority-0 part for scarce stock. The existing test
encoded the inverted rule and now asserts the host's direction.
Part geometry filtered only rapids, so scribe/etch moves counted as
material - the bug OpenNest fixed in 1b5e1b1. Use SpecialLayers.IsMaterial.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs with Claude have to happen on the Windows PC, where the archive is
mapped as Y:\Archive rather than hermes' /mnt/rogers mount.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Qwen3.8-Flash-Next's final version after a 14.5-hour optimization run
(its commit 7d7fca3): cost-first sheet trials, largest-area-first
demand order, and a cached-triangulation exact gate that brought a
219-part production job from timeout to ~106 s. 13/13 tests pass
against OpenNest master.
README cleaned for publishing: the model-facing template rules are
replaced by a one-line independence statement, the production job is
described generically instead of by its PEP job/file name (also in a
JobSolver comment), results show both sheet pools as re-measured here
(the 9-size claim in its report didn't reproduce: it grabs 96x240 and
under-fills them), and the stale StockLadder-crash note is gone now
that core leaves etch marks out of nesting.
Also drops a stale Aurora plugin reference from Opus55's README and
lists the engine in the repo README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rules for the runs were being made up mid-run (git for rollback, access
to the real-part drawing archive), so each model started under a
different rule set. BENCH-RULES.md writes them down in one place, stamped
into every new engine, so all models work under the same rules:
workspace limits and no searching for other engines, git init plus
commit-per-working-state, the archive as read-only with nothing
customer-identifying kept in the (publishable) engine folder, tests may
only be added to, and what the final report must cover.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was the starting point for the Qwen3.8-Flash-Next run, which now
continues as OpenNest.Engine.Qwen38FlashNext (stamped from _Template).
The unimplemented scaffold under the bare "Qwen" name would only
collide with or be confused for that engine once it lands here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OpenAI reuses model codenames across generations (GPT-6 reused
GPT-5.6's Sol and Luna), so a bare codename like "Astra" can't
identify which model built the engine. Prefixing the model version
keeps engine names unambiguous as more runs are added. The CLR type
is now Gpt6AstraNestingEngine, so benchmark reports show the new name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each engine-building run started from a hand-copied scaffold (Qwen's),
which carried that engine's name and stale notes. _Template holds the
generic scaffold with a __NAME__ placeholder, and New-Engine.ps1 stamps
out a named copy. The template's starter tests go through the
benchmark's NestValidator, so a model gets a real pass/fail target
instead of a plumbing-only check; they were verified to pass against
Opus55.
Directory.Build.props now also detects when it sits in an Engines/
folder inside an OpenNest checkout, so an engine stamped there with
-IncludeBuildFiles builds without passing OpenNestRoot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engines are moving out of OpenNest so they can be published on their own
and so OpenNest checkouts handed to a model for an engine-building run no
longer contain the competing engines. History for Engines/ (and the
pre-move root-level engine folders) was carried over with git filter-repo.
Engines now build against a sibling OpenNest clone via $(OpenNestRoot),
overridable with /p:OpenNestRoot, with a clear error when it is missing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the Terra scaffold with an independent configuration-space contact placer and bounded stock-plan search. Include plugin tests, synthetic and DXF benchmark drivers, results, and deployment documentation.
Correct shared collision clipping and hole subtraction so curved-hole inserts validate consistently. Cover translated layouts, spacing violations, operand order, winding, and independent boolean-area comparisons.
Validation: 1,293 tests passed with 12 fixture skips; all 34 synthetic/generated and four DXF cases are valid and complete.
Reusable Fill/, BestFit, RectanglePacking and CirclePacking components are
fair game; whole-engine delegation and run-all-pick-best stay banned.
Improvements to shared components go in the engine's own project and are
reported, not applied to OpenNest.Core/OpenNest.Engine.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Plugin engines (Opus55, Qwen, Terra) each add two projects at the repo
root, and more are coming; at a dozen they would outnumber the core
projects. They are also a different kind of thing: out-of-solution,
runtime-loaded plugins. Grouping them under Engines/ keeps the root
readable.
Engines/Directory.Build.props now holds the shared TFM, nullable and
implicit-usings settings and the OpenNest.Engine reference, so a new
engine's csproj is nearly empty. The tests/ compile exclusion lives in
Directory.Build.targets because a removal in .props runs before the SDK
adds its default Compile glob and has no effect.
Build-Engines.ps1 replaces the per-README manual build-and-copy steps
for deploying engines into the benchmark's runtime Engines/ folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rename the OpenNest.Engine.Sonnet5 scaffold to OpenNest.Engine.Opus55 and
implement an independent whole-job INestingEngine (no built-in engine,
registry, or best-fit internals are called or copied).
- PartCatalog: snapshot perimeter -> polygon per allowed orientation, with
adaptive chord tolerance and MBR-aligned rotations for Automatic parts.
- NoFitCache: spacing footprints and cached Clipper2 Minkowski NFPs (convex
fast path; concave sweep plus both containment terms).
- FrontierPacker: per-(type, orientation) free regions (inner-fit rectangle
minus NFPs), updated incrementally; gap-fill-largest, else least front
advance per area^beta.
- Engine: look-ahead stock choice by estimated whole-job net area, six
deterministic variants, tail re-plan of the last 1-3 sheets.
- Tests judged by OpenNest.Benchmark's NestValidator, including an NFP
containment regression guard.
P260805-10.nest (219 parts), all 9 stock sizes: 219/219 valid, 27 sheets,
91.7% utilization, ~7 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three standalone INestingEngine plugin projects (Solve() unimplemented,
throws NotImplementedException), each outside OpenNest.sln and discovered
at runtime via NestingEngineRegistry.LoadPlugins, same pattern as
OpenNest.Engine.Aurora. Each README spells out that Solve() must be an
independently designed placement algorithm and must never call/wrap/select
over the built-in engines (StockLadderNestingEngine, FixedStrategyNestingEngine,
PlateNesterFactory, NestingEngineRegistry).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>