Nest reports showed "-" for every part weight on N26-295 because the nest and
its drawings store Stainless Steel 310 with density 0: materials are picked by
name and grade, and nothing copies a density onto them. The densities live in
the machine material defaults, and for this nest not even on its own machine
(CL-980 lists only 304; 310 is on CI FIBER 4020).
The desktop now resolves a catalog density before capturing the report: the
nest machine's exact name and grade, any machine's exact name and grade, then
the same material name. A density stored on the nest still wins, and the
drawing's own material stays the last fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operators want plates of the same size grouped together so material
doesn't need sorting when it arrives; there was no way to change plate
order. Rows can now be dragged to a new position (with a drop line and
edge auto-scroll), or moved with Alt+Up/Alt+Down. Each move is one
undoable "Move Plate" step and only reorders: quantities are untouched.
The trailing empty new-plate sentinel stays last. An empty plate moved
to the end becomes the sentinel, as the sentinel rule already does on
reload, so the order shown is the order saved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The status bar showed only the material name and thickness, so nests
of the same material in different grades looked identical there. The
grade now follows the name, e.g. "Material: Steel A36 · 0.25 in".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The summary's parts table squeezed each thumbnail into a 62 pt column and
had no room for the size and weight the shop asks for. Each drawing is now
a card, four to a row: ID, a larger thumbnail, name, bounding-box size,
weight each and for all nested copies, required/nested counts and plates.
A shortage or extra is a badge beside the ID (SHORT in red) so it stays
easy to spot without a column to scan.
Weight is net area x nest thickness x material density, read as lb/in^3
like the machine material defaults; millimeter jobs report kilograms. A
nest without thickness or density shows "-" and a note saying what is
missing, never a zero weight.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export Nest Report only wrote a file, so checking or printing a report meant
finding it and opening another program. View Nest Report renders the same
snapshot to a private temp folder and opens it in a WebView2 viewer with
Save As and Print; closing the window deletes the temp copy. Export and View
share the capture and write guards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Group outlines and their shared ID were rejected whenever they touched
another part's bounding box. A large irregular part's box can cover most
of the sheet, so small repeated parts sitting in its notch or cutout
(e.g. test squares) were left with no ID at all. Test clearance against
real contours instead, as individual labels already do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The nest report joined contour segments only within 1e-12, but converted
arcs rebuild their end from the start radius and end angle. Rounded or
incremental programs (end radius a few 1e-7 off the start radius, which
the report's own validator accepts up to Tolerance.Epsilon) left every
arc a hair short of the next move, so outlines split into open pieces
and N26-27K's parts printed unshaded as if they had tab gaps.
Join within Tolerance.Epsilon instead, still far below ShapeBuilder's
welding tolerance and real tab gaps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delta review fixes:
- ReleaseRenest throws when a commit does not call finish, or calls it
twice: either would leave ledger plates uncoded or count a sheet twice
for later nests of the run. The ledger sheet count is the codes given.
- A stage whose layout is not kept now lists the copies its layout
missed with their own reasons (a part no sheet fits stays "does not
fit"); only the copies it placed are "the proposal cannot be kept".
- A valid ledger layout that places nothing says "Auto Nest placed
nothing on it" rather than claiming no part fits.
- ReleaseLedgerOffer rejects null rows; docs list every ledger note.
Review fixes for ledger-first Make Nests:
- The release renest (offer, staged run, codes, note) moves into
OpenNest.Desktop.ReleaseRenest, shared by the headless panel, the
live main window and the tests. Codes are given inside each commit, so
the live tab's Auto Nest undo step records them; before, undo and redo
dropped every ledger code.
- A bundle repeats one code per sheet: a later nest of the run lost the
whole bundle once an earlier nest took one sheet. Taken sheets are now
counted per code (LedgerCodeFilter.ExcludeCopies).
- A stage whose layout is not kept lists every copy it asked for as not
placed (the proposal cannot be kept), and a rejected ledger layout
says "MaterialLedger not used: Auto Nest could not keep a layout on
its stock" instead of a bare "No MaterialLedger stock used".
- "no free stock" is said only when the ledger had none; otherwise the
note counts what was left out (on other nests, taken by earlier nests
of this release, too small, unknown shape).
- The ledger is not read for a nest with nothing to nest, the live read
runs under the tab's stop token, a reason's own full stop is not
doubled, and the sheet count is the codes given.
With Nest after release ticked, one ReleaseLedgerStock is shared by the
whole run and passed to the renest step, headless or live in the main
window. The live tab runs one Auto Nest per stage (ledger stock, then
the Nest Defaults sheet), records the ledger locations for the plate
tooltips, and the group's note says how many sheets came from the
ledger or why it was not used.
NestRenest gains a staged run: a nest is nested on finite first stock
alone, then whatever is left on its plate defaults and plate-size
options. Each stage keeps only a valid layout, and the result lists the
stock id each committed plate was solved on.
ReleaseLedgerStock is the ledger stock of one Make Nests run: each nest
is offered the free sheets and drops Auto Nest's ledger load would tick
for its material, grade and thickness (substitutes included), minus
codes on other open server nests (read once per run) and codes earlier
nests of the same run took, which are not on the server until the
release uploads them. Committed ledger plates get their codes as Auto
Nest gives them. An unusable ledger nests on the plate defaults and the
note says why the ledger was not used.
The image HEALTHCHECK became /app/healthcheck.sh when the worker role was
added (54ad7ee8), but the container smoke still required the old inline curl
probe, so every Server image validation since then failed before publishing.
Expect the script form and describe it in the storage docs.
A cutout's lead-out ends in its slug, so the rapid after it always crossed
the cutout just cut and the strict search refused every plan with cutout
lead-outs. The slug is free and may drop or tip, so emission now marks that
rapid to raise the head, and the rapid check accepts a raised rapid that
starts at the lead-out's end and leaves its cutout exactly once. Cutouts
without a lead-out, or with an overcut, still leave from the cut head down.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last strip line stops at the remnant's full-length horizontal
separator, so it never counted as separating the sheet and the planner
cut it wherever the route passed it, often first (N26-264). That frees
the X-tail remnant early and loses the mass that absorbs heat while the
parts are cut.
A continuous line from a sheet edge or separator to another now counts
as a separator too (CutOff.Separators, found in rounds), so Plan Cutting
and Part Sequencing cut it after every part, just before the separator
it stops on. The problem-nest harness now adds scrap cut-offs with
KeepHorizontalRemnant, as both desktop entry points do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A blank Made By now starts from the name the user last typed there, or
their Windows user name when they never typed one. Changing the box on OK
remembers the new name in user settings; clearing it falls back to the
Windows name again. A nest's own author is shown unchanged and is not
remembered unless the user edits it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A nest window gave no sign that its nest had changed since the last
save, and closing it (or the app) discarded the edits silently. The
undo history already tracks a save point, so the title now ends in
" *" while the nest is off it (or holds an edit no scope recorded),
and closing asks to save, discard or cancel. Yes saves through the
storage in use; a database save cancels the close and closes the
window once the upload succeeds.
Saves now commit any unrecorded edit first so the save point covers
what was written. The window's cleanup moves from OnClosing to
OnFormClosed: another window can still cancel closing the app, and a
nest window must not lose its plate manager when that happens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Priority worked like a rank (lower first) with a default of 0, and the
Edit Drawing form stopped at 0, so moving one part ahead of the rest
meant renumbering every other drawing. Higher-first lets a user raise a
single part to 1 and leave everything else at the default.
Every engine, filler and candidate comparer now serves the highest tier
first. Stored values keep their order:
- .nest files add `nestPriority` and keep writing the negated value under
`priority`, so older readers nest in the same order; files without
`nestPriority` are negated on read. Drawing packages strip both, so
package hashes are unchanged.
- Library settings send both keys the same way for older clients.
- Server schema v18 negates stored drawing priorities (an older server
refuses the upgraded database).
- Job documents move to version 2; version 1 jobs are converted, and an
older worker refuses version 2 instead of reversing the order.
The Edit Drawing form now accepts negative priorities and says higher
numbers nest first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cut-off line renderer paired codes as rapid/cut at even indices. A
head-locked far-edge segment carries lock and unlock comments between its
cuts, so its overcut was never dashed and later segments could fall out of
step. Past the sheet only the dark part outline showed, which read as the
line having lost its orange-red color. Walk the motions instead and draw
each cut from the previous point.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A part with lead-ins applied could not be pushed away once another part
touched it. PartGeometry kept LEADIN/LEADOUT entities as material, so the
leads chained onto the perimeter as open spurs and the spacing offset no
longer closed. The slide-contact classifier cannot tell the material side
of an unclosed boundary and blocks every contact, so parts resting against
it could not move up or right off it. Leads are cut into scrap, and the
spacing review, overlap analyzer and support checks already drop them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "cut those two the other way round" rule is a guess. On an interlocked
holed-parts plate it swapped a pair whose swapped order then blocked on that
same pair; no further rule could be learned, the full search ran the strict
budget out, and Plan Cutting fell back to the safest route (0 strict
expansions), failing ContourRecoveryFileTests' interlocked rows since
16bd9256.
When a round teaches nothing and a swap was learned, the newest swap is now
taken back, never re-learned, and the attempt that taught it learns the plain
"cut this part before those" rule instead, backing up from that attempt as
any learned rule does. The swap is chosen apart from the plain rules, so a
"departed part first" rule with the same pair is never taken for a swap.
Both interlocked rows plan Ready within budget again. The problem-nest corpus
keeps every status and expansion count; a 4000-layout random sweep of 3-5
locked-lead rectangles keeps every status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tab entered at a fillet joint trims back around the corner, so the cut
stops on one edge and starts on the other. Each straight lead took its own
edge's normal and the pair sat 150 degrees apart, reading as one bent line
through the corner. Both now take the bisector of the two ends' normals and
form the same V they form on a straight edge, falling back to their own
normals when either lead would not clear the part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A support-tabbed part that no single tab can hold level may tip once cut,
yet its tabbed outside was crossable like any retained part, so Plan
Cutting could rapid back across a row of long strips. Its outside is now a
rapid obstacle, closed across its tab gap, in the search, replay, cut-off
insertion and the safest route.
Its fallback entries are ranked toward the next cut rather than steadiest
first, so a long strip can be left toward the next part, and the "cut those
two the other way round" rule applies only when the approach crosses no
other part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After its six variants and tail re-plan, Irregular decodes the job again
with each type of three or more copies packed as one group, in a searched
order, holding back up to four copies per group that would start a ragged
row and filling them into the gaps afterward. When everything fits, the
best candidates are re-packed in a strip ending just short of the front.
The pass runs after the ungrouped search, which is unchanged, and replaces
its result only by beating it. The help tutorial's parts now end at 34.9"
instead of 38.1" on 60 x 120.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the CL post declaring placed order for nests it writes as planned,
the help tutorial's nest must reach posting with no findings at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Cincinnati CL post always drew an "incomplete final rapid sequence"
warning because it never declared IPostVerificationSupport. It writes etch
features before cuts, inserts or drops rapids, and with part sub-programs
rapids to each part's corner, so it cannot declare it unconditionally.
IPostVerificationSupport gains a nest-aware PlacedProgramOrderChange(nest)
(null = preserved; otherwise the reason, shown in the warning). The CL post
answers it from its own writer: part sub-programs and smart rapids off, and
every part's and hole's posted motions exactly equal to its placed ones.
Plan Cutting cuts scribes first, so planned nests qualify. CNC output is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tabbed outside cut is trimmed short of its entry, but the trim only
handled lines and arcs: a round part's outline is one circle, so it was
cut whole. Plan Cutting counted it as tabbed and called the plan ready,
then the pre-post review found no tab and warned that the part may tip.
Cut a tabbed circle as an arc that stops the tab's chord short of its
entry, travelling the circle's own way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "Your first nest" layout (the help's sample parts, Auto Nested on a
60 x 120 sheet over a 2 inch support grid) must plan without warnings,
with support tabs on and off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round holes are cut as sub-programs, which are never marked because they
may be shared, so the safest-route fallback could not raise the head for
the rapid into a hole: on interlocked layouts every such crossing stayed
a finding and the plan stayed unverified. A HEAD RAISE marker may now sit
directly before a sub-program call, raising the call's arrival rapid.
- Motion reader: a marked call raises its first rapid; each sub-program
motion records the part's own call that runs it.
- RaisedRapids marks the call when the crossing rapid is its arrival.
- Cincinnati CL keeps the marker with its hole call and writes M47 before
it; part sub-program dedupe now tells marked programs apart.
- CI Fiber raises before the hole's first G0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Small parts on a coarse support grid (the help tutorial's 5 x 5 gussets
on a 2 inch grid) rest on one or two points, so no single tab could
steady them and every plan came back unverified. New cutting settings
now start "Tab alone holds parts up to" at 6; settings saved without the
field still read as 0, so existing settings keep their meaning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 5 x 5 right triangle fits both its legs' square and its hypotenuse's
7.07 x 3.54 box, each of area 25, and rotating calipers kept whichever
rounding found first, so the same gusset measured 5 long at one rotation
and 7.07 at another. Of rectangles tied for least area, measure the one
with the shorter long side, so length, width and the tab tie-break no
longer depend on placement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plans every occupied plate of every .nest file in a local folder the way
Plan Cutting does after clearing lead-ins and cut-offs, and writes one
JSON line per plate with its outcome, warnings, fallbacks, expansions,
time and spacing pairs. It is how the reliability sweep found its
failure classes over 1,260 production plates; the nests and the report
stay outside the repository, and the test skips without the folder.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contour recovery tries each outside entry's hole-tour search first for a
part with at most 16 holes, and the reading-order sweeps after them; once
the searched entries had spent the recovery allowance it jumped straight
to the widened tries and never swept. A frame cut from inside its own
cutout, after the strips nested there, is often routed only by a sweep,
which leaves the cutout on its far side toward the outside entry beyond
it, so such plates fell back to the safest route with head raises or to
no plan at all. The sweeps now still get their few expansions per hole,
bounded by the whole budget. On the production corpus six plates went
from a safest-route or head-raise fallback to a strict plan, five kept
fewer raises, one blocked plate gained a safest route, and none got
worse; the problem-nest corpus is unchanged in every outcome and share.
No synthetic plate reproduced the exhausted allowance yet, so the corpus
guards this change.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With a pierce diameter set, every internal contour whose configured
pierce is inside the zone is resolved by flattening the cutout, finding
its pole and eroding it, and the search emits a part's whole chain again
on every expansion, so on parts with dozens of such holes almost all of
the planning time went there: three 1,600-motion parts took 41 minutes
and timed out in production. The resolution is a pure function of the
hole, the entry, the configured lead-in, the zone and the turn, so each
preparation now keeps it by contour ordinal and reuses it, unchanged,
across its emissions and copies. The same plate plans in 148 s.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A fillet whose circle is tangent inside the arc it continues, as a
keyhole's slot ends are, gives the supporting-circle query of the pair
no real root: the circles' distance is the radii's difference up to
rounding, so the intersection comes back NaN, and the unfiltered check
that runs before any adjacency handling refused the whole part as
"nonfinite program coordinates". On the production corpus 17 plates
failed this way. Adjacent curves are now resolved from their shared
vertex before the native query would run, so no query runs for them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two curves that meet end to end share that vertex, but every contact
check asked the native intersection query about them, and it is
ill-conditioned there: two circles meeting at a shallow angle (a fillet
continuing into a larger arc) or a line meeting its fillet put their root
up to 1e-5 off the joint when the DXF arcs were rounded apart from their
neighbors by 1e-9. Material validation then refused the part as
self-touching, and the open-cut proof read a tabbed perimeter's fillet
joint as a loop, so with support tabs on every such part lost its
retention proof and its plate came back blocked. On a corpus of 1260
production plates this was the largest planner failure class: 61 of 67
NoSolutionWithinBudget plates, all planning once the joint is exact.
Contacts now report an adjacent pair's shared vertex itself and derive
the pair's other intersection from it analytically (two circles meet
again at the vertex's mirror image across the line through their
centers; a line meets a circle again at the far end of its chord),
keeping it only on both finite spans. No tolerance grows: a genuine
second crossing beside the joint is still reported, and overlapping
spans still refuse.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A server Auto Nest result could only be reviewed in the session that sent it,
and nothing stopped two desktops from keeping the same result. Phase 3 adds
My Server Jobs, so results that finished while OpenNest was closed are easy
to find, and records each review on the server.
- Server: GET /api/nest-jobs lists jobs newest first with state, engine,
submitter, times, queue position, review and the worker's summary (parts,
sheets, utilization, unplaced). It filters to active, finished or
unreviewed jobs and to a submitter (a bare user name matches that user on
any workstation). An ETag turns unchanged polls into 304s. Schema v17 adds
nestId, review and summary columns, only where missing.
- Keep once: POST .../keep and .../discard take If-Match on the job revision
and change an unreviewed result only once. A second desktop gets 409 with
who kept it. DELETE removes finished jobs. A succeeded job keeps its
package until it is reviewed, so a later session can rebuild the job.
- Retention: OPENNEST_JOB_RETENTION_DAYS (default 7) deletes finished jobs
with their results. It runs on claims and list reads. A malformed value
fails startup instead of deleting results early.
- Worker: reports the result's summary with the transcript. A job cancelled
while its result arrived keeps no summary (found in the Docker smoke run).
- Desktop: Nest > My Server Jobs polls the list every 3 s; polling rather
than per-job SSE because the list must show other desktops' jobs and
removals. Open rebuilds the job from job.json against the open nest's own
drawings. New per-drawing geometry fingerprints make the freshness check
work across sessions. Cancel Job and Remove act on the selected job. A
status-bar item counts this user's results waiting for review; there is
no popup. Auto Nest's review now records Keep on the server before it
commits, and records Discard too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A server Auto Nest showed only "Nesting on the nest server" for however long
the job took, and Stop could take a whole 20-second heartbeat to reach the
worker. Now the desktop shows the same Best plan so far strip a local run
does, and a job that ends while OpenNest is in the background says so.
- Worker: RecordingNestingEngine taps each solve's progress. NestJobReporter
stores only the latest stage and plan, at most once every two seconds, so a
chatty engine cannot flood SQLite. Each round also reads the cancel flag,
so Stop reaches the worker within one interval (about 2 s in the Docker
smoke test).
- Server: schema v16 adds per-job revision, progress, plan and planRevision
columns, which the upgrade adds only where missing. PUT .../progress and
.../plan renew the lease like a heartbeat. GET .../events is Server-Sent
Events, woken by an in-process change signal: plan and job events with
revision ids, keep-alives, Last-Event-ID resume, and the final state always
sent before the stream ends. GET .../plan serves the polling fallback.
Progress goes over HTTP because the worker never opens the database
(another host, NFS).
- Desktop: NestJobWatch follows the stream. When the stream drops it polls
once and reconnects, and it polls throughout when the server has no stream.
RemoteNestPlanPreview draws worker plans from the client's own request,
including cutout-trial frame proxies. ServerNestNotice shows a tray balloon
(a toast on Windows 10/11) when the job ends while OpenNest is not in
front. A real toast would need an AUMID shortcut that Velopack does not
promise. Clicking the balloon brings the review forward; nothing is kept
without Keep.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Large nests tie up the workstation that runs them. In Database mode, Auto Nest
can now queue the job on the nest server, where a worker container solves one
job at a time under its own CPU and memory limits. The job has no time limit,
so a throttled worker finds the same nest a fast one does, only later.
- Server: /api/nest-jobs queue (schema v15). The worker holds a job on a
heartbeat lease. When the lease lapses, the job goes back to the queue,
or fails after three attempts. Only one job runs at a time. Packages and
results are opaque to the HTTP server, which stays off Engine and IO.
- Worker: OpenNest.Server.Worker in the same image (OPENNEST_ROLE=worker),
reaching the server over HTTP only. It records each engine answer with
RecordingNestingEngine and gives its job back on shutdown.
- Desktop: "Nest on the server" in the Auto Nest dialog, for built-in
engines only. The client replays the recorded answers with
RemoteNestSubmission, so validation, the review and Keep run locally
against the client's own drawings, exactly as after a local run. A
drawing edited after sending refuses the result. If the server cannot
take the job, the user may nest locally instead.
- compose.server.yaml adds the throttled worker service. The Dockerfile,
.dockerignore, CI paths and the deploy script now include the worker's
projects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reopening a nest regenerated its cut-offs with built-in defaults (no
overtravel, a different minimum segment) rather than the machine's
settings it was made with, so a nest saved, reopened and posted could
cut scrap that never separates. Plates now record the settings their
cut-offs were last generated with, and files carry them; older files
still load with the defaults.
Saving over a nest file truncated it before writing, so any failure
part way lost the only good copy, and the error reached the user as an
unhandled exception after the save path had already changed. Nest and
settings files are now written beside the target and swapped in, and a
failed save is reported with the old file left intact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ahead of the new laser schedule, three ways a post could silently cut
with the wrong process, and one way its settings silently reset:
- Cincinnati CL wrote no G89 when no cut library matched the nest's
material, thickness and gas (or no etch library for etch features),
so the controller cut with whatever library it loaded last. It now
refuses, unless the new "Require libraries" setting is turned off.
- Cincinnati CL declared G20/G21 from its own setting without comparing
the nest's units, so a metric nest posted 25.4x too large. It now
refuses a mismatch.
- CI Fiber posted any material missing from its table as the default
code (MSN), so stainless or aluminum ran with mild-steel technology.
A named, unmapped material is now refused; the default code covers
only nests with no material.
- Posts saved their settings beside their DLL, inside the install
folder a Velopack update replaces, so every update reset them to the
shipped defaults. Saves now go to %APPDATA%\OpenNest\Posts and the
shipped JSON only seeds them; constructing a post no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PartSupport findings counted toward the headline and the Post Anyway
checkbox but had no tile or list group, so an operator saw "1 warning"
with nothing to read and was likely to tick through a part that may tip
once cut free. A test now fails if any finding kind is left unlisted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A support-tabbed part that no single tab can hold level was limited to its
one steadiest entry. Two strips stacked in a frame's cutout then blocked
the plate: from the upper strip's only entry, every route to the lower one
crossed holes already cut. Its other entries clear of inside corners now
follow the steadiest, in the search and in contour recovery; the part is
still tabbed and still warned about. Parts a tab can hold keep their
steadying entries.
The unverified headline also named fixed lead-ins when the only warning
was a part that may tip; it now says what needs review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every part the support grid could not hold level got a tabbed perimeter
with the outside lead-out after its tab, and planning refused any lead-out
after its own open cut, so with a lead-out configured no such part could
be planned. Pre-post review already proves such cuts release nothing (the
lead-out stays out of the tab gap); planning now runs the same proof, so
planning and review agree and a lead-out that reaches into the tab is
still refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The grid was added to check the whole library for drawings that freeze
a preview; it is a testing tool, not an operator feature. The form and
its test stay for future library checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>