Commit Graph
1728 Commits
Author SHA1 Message Date
ajandClaude Opus 5.5 6cdda38d35 fix(verify): pass tabbed cutouts that provably retain their slug
A cutout cut in several pieces around tabs, as PEP programs often are, made
pre-post review report every piece's lead-out and the part's open fragments
as incomplete. Review now proves such open cuts release nothing: within the
part they touch other cuts only at their own ends and form no loop (each
closed contour with its leads counting as one point), and leads at open ends
stay out of the gap beyond them. Anything else stays incomplete. Plan Cutting
keeps refusing to certify its own tabs followed by a lead-out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 00:15:06 -04:00
ajandClaude Opus 5.5 3672697cb2 fix(ui): fill tabbed cutouts as one hole
A cutout cut in pieces around tabs is a set of open cut figures. Filling the
cut outline closed each piece with a chord, drawing a slanted bridge across
the hole. The fill path now rejoins open figures across their tab gaps, and
parts with open cuts use it for drawing, hit-testing and the lead-in overlay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 23:40:11 -04:00
ajandClaude Opus 5.5 af65cb178b feat(ui): remember status-bar location mode as a user setting
Clicking the location label now saves the mouse-follow/click-only choice
to StatusBarLocationFollowsMouse (default true), so it survives restarts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 23:13:02 -04:00
ajandClaude Opus 5.5 4176e9d89a perf(ui): throttle status-bar location and follow the mouse by default
Mouse-follow mode set the auto-sized location label on every MouseMove,
relaying out the whole status strip and slowing the UI. Updates now run
at most every 100 ms with a trailing update so the label settles on the
final point. Mouse-follow is now the default; clicking the label still
toggles click-only mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 23:11:08 -04:00
ajandClaude Opus 5.5 766584633e refactor(ui): redesign Edit Drawing dialog
Rebuild the dialog as a two-column layout: a larger preview with a
color swatch, a Change Color button and read-only size, area and
source-file details on the left; Drawing (name, customer) and Nesting
(quantity, priority, cut first) groups on the right.

Quantity shows how many parts are already nested and how many remain,
priority explains that lower numbers nest first, and cut first gets a
short description. The form now uses Segoe UI with font auto-scaling,
re-renders the preview when its size changes, selects the name on
open, and is titled "Drawing Properties - <name>".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:58:50 -04:00
ajandClaude Opus 5.5 8f73704bef feat(server): migrate v4 drawing library data to schema v5
The v4 upgrade now keeps existing library data: each distinct package
blob is stored once by hash, every revision and current row is copied
with default nesting settings, and the v4 drawing tables are dropped,
all in the migration transaction. Pre-v4 databases get the v5 tables
directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:52:25 -04:00
ajandClaude Opus 5.5 5a374aa3f4 feat(library): content-addressed packages and editable nesting settings
Schema v5 stores each distinct drawing package once in drawing_packages,
keyed by SHA-256; revisions point at it, so details, settings, archive and
restore revisions add no blob. The v4 drawing tables are replaced outright
(no production library existed); nest tables are untouched.

Nesting settings (cut first, priority, rotation constraints, color) become
structured record fields (DrawingLibraryRecord.Settings), returned on every
read, filterable by cutFirst, editable through the metadata endpoint and
applied by LibraryDrawingInsert. Canonical packages now leave every
record-owned field at its default, so renaming or changing settings never
changes the package hash. Saving a nest drawing to the library carries its
settings into the record.

The desktop library browser gains a Cut first column and an Edit Settings
dialog that saves notes and settings as a new revision with the same package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:47:18 -04:00
ajandClaude Opus 5.5 42d99f296c feat(sequencing): add per-drawing Cut first priority
Drawings flagged CutFirst (e.g. test squares) are sequenced ahead of
every other part: they get their own route with the chosen method, then
the remaining parts follow. Cutoffs crossing a cut-first part still
precede it. The flag is editable in Edit Drawing, persists in .nest as
`cutFirst` (omitted when false) and is kept by split pieces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:19:03 -04:00
ajandClaude Opus 5.5 7a1a554804 fix(ui): center parts within the plate work area
Center Parts centred the parts on the whole plate, ignoring edge
spacing, so a wider clamp-side margin left the parts closer to the
clamps than the opposite edge. It now centres them in Plate.WorkArea()
via the new Plate.OffsetToCenterPartsInWorkArea, so each side keeps
the same extra room beyond its edge spacing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:09:07 -04:00
ajandClaude Opus 5.5 3541249441 refactor(ui): collapse MainForm menu delegators
Adds WithActive for handlers that only forward to the active nest
document and collapses 32 of them to one line. The eight align and
evenly-space items share Align_Click, reading their AlignType from Tag.
Center Parts becomes CenterParts_Click and ignores a missing plate or
an empty plate instead of throwing. The hidden, disabled Paste item
loses its empty handler. SetValueForm, OptionsForm and ShapeLibraryForm
are now disposed and owned by the main window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:16:57 -04:00
ajandClaude Opus 5.5 93d118585f refactor(ui): extract MainForm status bar binding
MainStatusBar owns the status, location, selection and plate labels and
the click-vs-move location mode. Bind(EditNestForm)/Unbind() replace the
manual PlateView subscribe/unsubscribe pairs in OnMdiChildActivate;
MainForm keeps only its overlap-menu subscription. Applying the location
mode now detaches both mouse handlers first, which also drops the old
self-assignment of the mode flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:14:18 -04:00
ajandClaude Opus 5.5 9b46db4891 refactor(ui): extract MainForm startup and settings services
Moves startup and settings code out of MainForm:
- PostProcessorLoader discovers posts and registers their materials.
  Each DLL now loads in isolation; a failing one is named in a startup
  warning instead of throwing out of the MainForm constructor.
- MachineSettings owns the clamp-settings and nest-defaults entry points
  and their once-per-session warnings; BomImportForm calls it directly.
- NewNestFactory creates File > New nests and their daily names;
  GetNestName is now a pure static the characterization test calls.
- MainWindowSettings restores/saves window placement and migrates the
  legacy nest template.
- UpdaterStatus owns the updater and its status-bar item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:11:37 -04:00
ajandClaude Opus 5.5 3cae257fb1 refactor(ui): extract MainForm Auto Nest, post and storage workflows
Moves the long workflow handlers into OpenNest/Forms/Commands:
AutoNestCommand (dialog, owned modal progress, solve, validation review,
commit and result notices), PostCommand (prepare, configure, verify,
save dialog, multi-file overwrite confirmation) and NestStorageController
(database open/save/save-as with reconciliation and storage errors).

MainForm keeps the busy guards, the nesting lockout and nestingCts, and
reacts to the controller's StateChanged to lock storage settings and
saving. The characterization test now flips the controller's IsSaving
instead of the removed databaseSaveInProgress field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:07:13 -04:00
ajandClaude Opus 5.5 e6b1e0308f refactor(ui): consolidate MainForm busy guards
Moves every MainForm busy predicate into MainForm.Guards.cs, built on
IsJobRunning, ViewsOf, IsPlateWorkBusy and IsNestFillBusy. Auto Nest,
Validate and Post share TryBeginPlateCommand; Clamps, Automatic Cutoffs
and Plan Cutting reuse the shared predicates. The deliberate variants
(Expand Spacing ignores busy actions; the report guard adds database
saves) keep their names and remain pinned by characterization tests.
NavigationEnableCheck now takes the lockout flag instead of
SetNestingLockout repeating its logic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 20:57:26 -04:00
ajandClaude Opus 5.5 d87d113ceb refactor(ui): split MainForm into per-menu partial files
Moves members verbatim into MainForm.File/View/Tools/Nest/Plate/Status
partials (nested under MainForm.cs in the project); MainForm.cs keeps
the shell: fields, startup, overrides and the Window menu. Each file
keeps only the usings it needs. No logic changes; private member names
used by reflection-based tests are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 20:57:26 -04:00
ajandClaude Opus 5.5 686bb4d749 test(ui): characterize MainForm nest naming and busy guards
Pins the nest-name format and which operations each MainForm busy
guard counts, ahead of splitting and consolidating MainForm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 20:57:26 -04:00
ajandClaude Sonnet 5 9d6c10c563 feat(ui): add Parts tab listing pass/fail status for every part
Lists every part in the cutting inspection panel's scope, including ones
with no finding, so an all-clear nest doesn't just look like an empty
grid. Selecting a part jumps to its first issue or zooms to it when clear.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 20:54:24 -04:00
ajandClaude Opus 5.5 6f555345e3 feat(cad): cut breakoff joining lines as tabbed open break cuts
Breakoffs are now picked by their joining line, default to the smaller side as scrap
(flippable), and cut that line between tabs on a new Break layer instead of slots.
Plan Cutting and Assign Lead-ins enter each cut from the scrap with the nest's internal
lead-in, or pierce on the line without one; checks treat break cuts as intentional open
cuts that are not material.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 17:46:35 -04:00
ajandClaude Opus 5.5 6fd14c5452 feat(cad): tab breakoff scrap to the part in the CAD Converter
Forming backgauge tabs drawn as open cuts came off the sheet loose and could not be
planned. A breakoff merges the scrap into the outline and cuts the shared edge as
closed slots between tabs, so it stays attached and every pierce lands in scrap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:42:37 -04:00
ajandClaude Sonnet 5 ca09c33c38 feat(ui): add Open in CAD Converter to drawing context menu
Lets a single drawing be opened and edited in the CAD converter
directly from the drawing list, reusing the existing apply-back
logic from the Edit Drawings button (now shared via
EditDrawingsInConverter).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 14:59:28 -04:00
aj adc12e175d Add drawing library toolbar icon 2026-10-17 14:34:41 -04:00
aj ac161f93c4 Merge remote-tracking branch 'origin/master' 2026-10-17 12:57:36 -04:00
ajandClaude Opus 5.5 bc363cc904 feat(engine): name every unplaced part and why
NestPipelineResult.Unplaced lists each requirement's unplaced copies with a
typed NestUnplacedReason, counted from the returned placements: no stock fits
at the sampled rotations, reserved in a cutout of an unplaced frame, stock
exhausted, plate limit, no position found, or proposal not keepable. Desktop
Auto Nest, Console --autonest and MCP autonest_plate/autonest_job report them;
the API returns NestResponse.Unplaced, persisted from response schema 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:51:24 -04:00
ajandClaude Opus 5.5 4a1587d6bd feat(engine): fill cutouts first in every whole-job run
NestPipeline.Run now bundles parts that fit other parts' enclosed cutouts
into their frames before the selected engine, for desktop, Console, MCP and
API alike. A valid complete bundled trial is kept; a refused trial falls back
to the ordinary solve (reason on RefusedCutoutTrial), and an incomplete trial
is compared with it, keeping whichever valid proposal places more. An
incomplete but valid trial is now keepable like any incomplete result.
Expanded sheets list each frame's inserts before the frame, so the committed
order cuts every insert before the cutout that releases its slug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:51:18 -04:00
ajandClaude Opus 5.5 6ac56c4a3f feat(core): pre-post checks for parts nested in cutouts
Posting review now proves containment exactly as the cutting planner does
(shared NestedPartContainment) and warns when a nested part is cut after its
host (NestedCutOrder) or when a host's hole lead or the nested part's lead
meets the other's material (NestedLead), including subprogram hole poses.
Ambiguous containment is incomplete, never clear; fixed-lead motions are not
reported twice. A committed cutout preview warns until Plan Cutting cuts the
insert first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:09:55 -04:00
aj 0cef0ae297 test(desktop): establish keyboard focus before saved-nest Enter
Fix the focus-sensitive failure in HighlightedNestShowsItsPlatesAndDrawings_AndEnterOpensIt from GitHub run 39042902952. Explicitly focus the nest grid and assert it owns focus before sending Enter. Production keyboard handling and all existing open, cancel, and missing-archive assertions remain unchanged.
2026-10-17 11:32:07 -04:00
ajandClaude Sonnet 5 b324baa69a fix(desktop): keep scrap cut-offs checkbox selectable during planning
The cut planner disabled "Add scrap cut-offs" along with every other
control while a plan ran, including the very first auto-started plan.
It now stays enabled; toggling it mid-plan cancels the run in progress
and restarts planning with the new setting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 01:59:34 -04:00
ajandClaude Opus 5.5 170b517a28 refactor(engine): share engine-name classification across front ends
Console and Mcp each re-derived whether a name was a fill strategy, a
whole-job engine or unknown. EngineNameResolver now owns that decision,
trims consistently and accepts renamed legacy engine names; front ends
keep their own message wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 01:45:16 -04:00
aj 9360f59077 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	OpenNest/Forms/CuttingInspectionPanel.cs
2026-10-17 01:34:22 -04:00
ajandClaude Opus 5.5 2f91f1b036 fix(engine): forged pinned-contour ordinal stays an Incomplete verdict
The 5.5 pinned-entry shortcut in ContourEntryFeasibility looked up the
choice's contour before the probe's own validation, so an owned choice with
an out-of-range contour ordinal threw instead of returning Incomplete as it
did before. The lookup now runs only for an ordinal inside the preparation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:27 -04:00
ajandClaude Opus 5.5 6c691cc23b test(desktop): block the all-plates panel test on overlap
Since 5.2 a locked program's own rapid crossing is a fixed-lead warning on an
UNVERIFIED plate, so it no longer makes the panel's "Can't apply" case. The
blocked plate now holds overlapping parts, a hard block, and the test checks
the same navigation and no-partial-Apply behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:26 -04:00
ajandClaude Opus 5.5 9816326429 fix(engine): legacy lead assignment respects pins
Assign Lead-ins (LeadInAssigner) now leaves every unlocked part with active
drawing pins unchanged and returns those parts, so it never overwrites a pin
with the plate's settings; the desktop names them ("N parts kept their
drawing lead-ins (plate P: parts ...); use Plan Cutting to apply their
drawing pins."). Parts without pins, with pins switched off, or locked are
assigned or skipped exactly as before.

The pre-post review now covers drawing pins. A part with active pins whose
placed program does not cut each pinned contour exactly as the pin's own
emission (re-emitted with the part's cutting settings and matched motion for
motion) warns "Drawing pins not applied", as do locked lead-ins overriding
pins and a pin that no longer matches the geometry; pins switched off are
not reported. The contours that are cut as pinned join locked programs in
the fixed-lead re-check against every part's clean material, and fixed
leads are now also checked against active clamps (Plan Cutting itself does
not model clamps).

AGENTS.md records the fixed-emission safeguard and the characterization
digests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:26 -04:00
ajandClaude Opus 5.5 a18f8c9b56 feat(engine): pinned contours use the fixed-lead warning path
A drawing-pinned contour is now a fixed emission like a locked program. A
regenerated placement maps its execution motions onto the emitted contour
choices (one group per choice after the program's scribes, opened by its
rapids) and owns as fixed the lead-in, cut, lead-out and tab of every pinned
contour; the rapids between contours, including the one into a pinned
contour, stay the planner's and strictly checked. If the motions do not
split cleanly into one group per choice, nothing is treated as fixed.

So a pinned lead that meets another part's material or its own part's no
longer rejects the candidate: the feasibility precheck accepts a pinned
entry as given, the search keeps it in place, and replay reports a FixedLead
warning naming the part, cutting contour, pin and the part hit, with the
advice to increase spacing or switch off drawing pins for the part. The
plate is UNVERIFIED and needs acceptance; the lead is never shortened and
the entry never moved. The unpinned control and the same plate with pins
off are Ready.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:25 -04:00
ajandClaude Opus 5.5 8d17cf8c59 feat(engine): capture and freshness for pins
Capture now honours a placement's pins only while they are active: a part
with DrawingPinsOff plans from the plate settings with no warning (D1's
per-part escape hatch), and Part.HasActiveDrawingPins is the shared test.
A pinned part is regenerated like any unlocked part whatever lead-ins or
settings it carried. A locked part with active pins keeps its exact program
and warns "Locked lead-ins override drawing pins on part N; Clear Lead-ins
to apply the pins." instead of being refused; an unlocked part a caller left
ineligible warns that its pins are not applied.

Apply's freshness record now holds each drawing's pin list (reference and an
explicit StateFingerprint of every pin's point, leads and tab) and each
part's pins-off flag, so editing either after capture returns Stale. The
fingerprint writer is shared between settings and pins; a reflection test
pins ContourCutPin's public surface to what the fingerprint covers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:25 -04:00
ajandClaude Opus 5.5 7949d41b6d feat(core): pinned contour preparation
PreparedContours.Capture(program, parameters, pins) takes CutPinResolver
results for the same contours and fixes every resolved pin's contour: its
one and only entry is the pin's point, and it is emitted through the
existing emitter with the pin's own lead-in, lead-out and tab (angle
rounding off, so the entry never moves). Every catalogue returns the pinned
entry alone and emission refuses any other entry for that contour, so no
search path can relocate it. The certified tab size is the pinned outside's
own, so a pinned tab gap is genuine and replay certifies it. Unpinned
contours keep the shared settings and automatic selection; without pins the
existing preparation is untouched (5.1 digests unchanged).

Capture now resolves a regenerated part's drawing pins. A pin that does not
resolve (not on a contour, on several, sharing one, or unplaceable) is never
guessed: its contour stays automatic and the plate gets a FixedLead warning
naming the pin, making the proposal UNVERIFIED. A tab pinned on an internal
contour cannot be cut as pinned (only the outside is tabbed) and warns the
same way. Pins-off, locked parts with pins and freshness follow in 5.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:24 -04:00
ajandClaude Opus 5.5 d56f67a99a feat(engine): fixed-lead violations warn instead of refusing
D1a (user, 2026-10-14): one locked cutout must not refuse the whole layout.
With confirmed parameters a locked program is now a fixed emission, accepted
as given. The replay checker and the lead validator take a per-motion owner:
a violation inside the fixed emission (a lead meeting another part's or its
own material, a rapid inside the locked program crossing a cut contour, an
uncertifiable tab gap or a partial/reversed/retraced cut) is recorded apart
instead of failing the candidate. The search never rejects or learns an order
rule from them, so fixed emissions never steer it; the planner's rapid into
the locked program and every regenerated lead keep every strict check.

Replay turns those violations into typed FixedLead findings that name the
part, contour and the part hit. A plate whose only findings are these is
BestEffort with IndependentlyReplayed set: never Ready, applicable only with
the per-proposal acceptance, and CuttingPlanService.Apply still rejects it.
Overlap, unreadable programs, stale input and cancellation still block, and
unlocked programs a caller left ineligible and the no-parameter route keep
refusing as before.

Pre-post review now re-checks locked leads against every part's clean
material, so the same violations reappear before posting.

Existing tests that encoded the old locked refusal now assert the warning;
two batch tests that used a locked crossing as their hard block use an
overlap instead. The 5.1 digests are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:23 -04:00
ajandClaude Opus 5.5 36ba3c2d6b test(engine): planning characterization
Freeze what Plan Cutting produces today before pins and fixed-lead warnings
touch the planner: the six synthetic nests (placed deterministically, every
other part turned a quarter) and five focused fixtures go through the desktop
batch, and the proposals, expansions and every emitted motion are hashed.
Coordinates are rounded to six decimals so last-bit libm differences between
platforms cannot move a digest. OPENNEST_PLANNING_DIGESTS appends the computed
digests to a file for the slice record.

Two red rows record today's refusals that D1a will turn into warnings: a
locked hole lead that runs into a part nested in that hole, and a locked
tabbed perimeter under confirmed parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:22 -04:00
ajandClaude Opus 5.5 d9b8f054cc feat(core): mismatch drop and part copy
Two remaining pin rules from the drawing library plan:

- D3: a library drawing added to a nest whose material, grade or
  thickness differ from the library record now really arrives without
  its cut pins. The insert already warned and reported KeepPins = false;
  it now clears the copy's pins, because they were authored for the
  library material and thickness. The library link still records the
  revision the copy came from.
- A copy of a placement (Clone, CloneAtOffset) keeps its drawing pins
  off, so copying or patterning an overridden part does not silently
  bring the drawing's pins back. Clearing lead-ins keeps it too: pins-off
  is a placement choice, not lead-in state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:22 -04:00
ajandClaude Opus 5.5 734346eeec test(core): pin frame lifecycle
Proves the source-to-program mapping the pins rely on (plan policy: prove
it or stop): the program frame is the source frame moved by
-Source.Offset, never rotated in place; a placement rotates it about the
program origin and adds the part location. Source.Angle is canonical
metadata and plays no part.

The oracle does not use Source.Offset or part rotation bookkeeping. It
recovers the rigid source-to-plate transform from the placed geometry
itself, using two holes of different sizes, and checks every pin at 0,
90 and 37 degrees, placed directly and the way engines place parts
(CreateAtOrigin), after:
- import (BuildDrawing re-origins at the first rapid);
- a converter edit that hides the first-cut hole and so moves
  Source.Offset;
- a converter edit of a drawing without source entities (the derived
  entities are moved back by Source.Offset);
- save and reload;
- a drawing package round trip.

To exercise the real converter paths headlessly, the desktop's entity
load and in-place apply move into CadImporter.EditableEntities and
CadImporter.ApplyEdit, unchanged; the in-place apply keeps the library
link and cut pins as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:21 -04:00
ajandClaude Opus 5.5 0f06748f28 feat(io): persist pins and pins-off
Drawing cut pins and the per-placement pins-off flag now survive
save/reload and travel in drawing packages.

- `DrawingDto.cutPins` reuses the plate cutting-parameter lead and tab
  DTOs. Pins are read strictly per entry: an unknown lead or tab type is
  never replaced by a default, because a pin that cut differently from
  what was saved is worse than no pin. A bad entry is skipped with a
  reader warning naming the drawing and pin, and the rest load; a value
  that is not a list loads no pins, with a warning. A nest never fails to
  open over pins.
- `PartDto.drawingPinsOff` is written only when true. Anything other than
  a boolean loads as pins on (the drawing wins, D1) with a warning naming
  the plate and part, so an operator's override is never lost silently.
- Packages carry pins as drawing content, so pinning a drawing changes
  its package hash and the newer-revision notice can tell geometry/pins
  changes from detail-only ones.
- `Part.DrawingPinsOff` holds the flag; nothing plans with it yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:21 -04:00
ajandClaude Opus 5.5 1bd0b258af feat(core): contour cut pins
A library drawing can say how individual contours must be cut. A
`ContourCutPin` holds the entry point in the drawing's source frame plus
the lead-in, lead-out and optional tab used there; `Drawing.CutPins`
lists them. Nothing plans with pins yet (Slice 5).

- Pins accept only the exact built-in lead and tab types, copied through
  the same owned copiers the planner uses, so a pin cannot carry a type
  the emitter would not certify. Properties return copies: the pin is
  immutable.
- A tab requires no lead-out, because the planner refuses a lead-out
  after an open contour.
- The source frame is the frame of the source entities. It does not move
  when BuildDrawing re-origins the program at its first rapid, so pins
  survive converter edits that change Source.Offset.

`CutPinResolver` maps pins to (contour ordinal, entity ordinal, local
point) for a placement rotation, using the same `PreparedContours`
ordinals Plan Cutting will use. It never guesses: a pin must lie within
the contour closure tolerance of exactly one contour that no other pin
claims, otherwise it is reported unresolved with a typed reason. A
drawing program rotated in place (a canonical copy) is not the source
frame and resolves nothing; canonical copies and split pieces carry no
pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:20 -04:00
ajandClaude Opus 5.5 2aecafb356 feat(desktop): Save to Drawing Library dialog
Operators can now save a nest drawing to the shared drawing library. Right-
click a drawing in the drawing list > Save to Drawing Library... opens a
dialog with name, customer, material (post materials suggested), grade,
thickness, cut type and notes, prefilled by the save model; units follow the
nest.

Whenever the details change, the dialog looks up library drawings with the
same details and offers Save as: the next revision of the match (the default
for one match or the linked drawing), a choice among several, or a separate
drawing. Save stays disabled until the lookup for the current details has
finished, so a choice can never apply to details it was not made for. A 409
is shown and the choices are refreshed; a save that could not be confirmed
tells the operator that saving again checks it and never stores it twice.

On success only the drawing's library link changes, and the chosen cut type
is remembered per PC. There is no nest dirty flag to set, so the operator
saves the nest to keep the new link. The item is Database mode only and is
shown only where the drawing list's host handles it (not the best-fit viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:20 -04:00
ajandClaude Opus 5.5 f93b0d6273 feat(data): library save model
Saving a nest drawing to the drawing library now has a Linux-testable model,
DrawingLibrarySaveSession, ahead of the desktop dialog.

Prefill starts from the drawing's name and customer (the nest's customer when
the drawing has none), the nest's material, grade, thickness and units, and the
linked revision's cut type and notes when the drawing came from this server's
library, otherwise the operator's last-used cut type.

Duplicates follow D4: active drawings whose normalized customer, name, material
and grade and exact thickness, units and cut type match. The server only
narrows (a blank filter means "any" there, and LIKE does not collapse
whitespace), so every key is checked again on the client. The default is the
linked drawing when it matches, else the only match; several matches require
the operator to choose, and a linked drawing that no longer matches is offered
but never the default, so a thickness change cannot silently rewrite it.

Writes follow NestSaveSession: a new drawing's id is allocated before the
upload, and a save that fails without a definite answer stays pending. The
next save first looks for the revision it would have produced and adopts it
when the package hash and details match; a not-stored new drawing is retried
under the same id, never onto another drawing, and a revision someone else
saved meanwhile is a conflict. A definite refusal (409, old server, invalid
request) leaves nothing pending. On success the drawing's link points at the
saved revision; nothing else in the nest copy changes (D2).

RemoteDrawingLibrary now implements IDrawingLibraryStore, the read/write
surface the session needs, so tests can use an in-memory store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:19 -04:00
ajandClaude Opus 5.5 a1bd44d0fa feat(desktop): Add from Drawing Library
Operators can now pull shared library drawings into a nest. Nest > Add from
Drawing Library... and a Library button above the drawing list open a paged
browser (name, customer, material, grade, thickness, cut type, revision, saved)
with a notes preview and a quantity. It starts filtered to the nest's material,
grade and thickness, with Show all to lift that.

Add downloads the listed revision's immutable package, not the current one, so
a revision saved meanwhile can never be what lands in the nest. The insert is
planned first and every warning or refusal is shown before the nest changes;
the dialog is modal, so the nest cannot move between plan and apply. The dialog
stays open for further adds and the drawing list refreshes on close.

Both entries are Database mode only: the library lives on the nest server, so
in File mode they are disabled with a "Database mode only" tooltip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:19 -04:00
ajandClaude Opus 5.5 175782d8f8 feat(data): library browse session
Add from Drawing Library needs the same race-safe paging as the Database-mode
Open dialog: a generation counter so an older response never replaces a newer
one, cancellation after the superseding request is installed, and IsLoading
tracking only the latest request. `DrawingLibraryBrowseSession` mirrors
`NestBrowseSession` for library queries.

It starts filtered to the nest's material, grade and thickness, since that is
almost always what the operator wants and a mismatch drops cut pins anyway
(D3). "Show all" lifts the filter. A nest with no thickness filters by material
only, and the empty-result text tells the operator when the nest filter, rather
than an empty library, is why nothing is listed. Archived drawings stay hidden.

`IDrawingLibrary` is the read side the session and the add flow use, so both
are testable with fakes; `RemoteDrawingLibrary` implements it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:18 -04:00
ajandClaude Opus 5.5 4dbb4527e2 feat(data): library insert model
Adding a library drawing to a nest has several outcomes that must be decided
before anything changes, so the desktop can show warnings first and so every
rule is tested on Linux. `LibraryDrawingInsert.Plan` returns a new exact copy
carrying its library link, an added quantity on the copy already there, or a
refusal that leaves the nest untouched.

- Material, grade or thickness that differ from the nest still add the drawing
  but drop its cut pins, with one warning naming each difference (D3). Names
  compare by SharedListNames key and thickness at four decimals, matching the
  server's own filters.
- Different units are refused: conversion is out of scope and a silent unit
  mismatch would scale the part.
- The same revision from the same server only raises the quantity; a different
  revision of that drawing is refused in favour of Update Drawings (D2b).
- Another drawing with the same name is refused naming both, because the
  name-keyed DrawingCollection would otherwise drop one silently.
- A package whose hash or units disagree with its record is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:18 -04:00
ajandClaude Opus 5.5 15654c0690 feat(core,io): drawing library link
A nest drawing copied from the drawing library has to remember exactly which
revision it is a copy of, so later slices can tell when the library moved on
(D2b) and so a save can target the right drawing. `Drawing.Library` holds the
server key, id, revision and a snapshot of that revision's cut type, thickness,
material, grade, units, notes and package hash. It is immutable, so copies can
share it.

`DrawingDto.library` is additive and omitted when null. It is read leniently:
anything that cannot be trusted as a link drops the link with a reader warning
and the drawing still loads, because a nest must never fail to open over
provenance. Enum names are checked as single names since Enum.TryParse would
accept numbers and comma lists.

Copy paths: converter edits update drawings in place and so keep the link;
canonical engine copies carry it; split pieces are new geometry and get none.
Packages strip it so linking never changes a drawing's package bytes or hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:17 -04:00
ajandClaude Opus 5.5 8b63c042b5 test(server): drawing library end to end
Proves Slice 1's contract through the production routes and the real client:
a code-built drawing is packaged, uploaded, found by cut type, material, grade
and thickness among decoys, downloaded and read back with identical program
text; a stale If-Match update raises a conflict and leaves the current
revision, its package hash and the history unchanged. Archive, restore,
metadata edits and duplicate ids are exercised the same way.

Documents the /api/drawings contract, schema v4 and the fact that the backup
manifest and restore check do not yet verify drawings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:03 -04:00
ajandClaude Opus 5.5 0c1eb3ad65 feat(data): drawing library client
RemoteDrawingLibrary covers upload, query, get, package download, revisions,
revision packages, guarded package/metadata updates, archive and restore. It
checks the shared bounds before sending, refuses oversized or inconsistent
pages, and verifies that the server stored the exact package it sent.

A 404 on the library routes means the server predates them: the client raises
ServerTooOldException and never falls back to another store. Because a 404 for
one drawing is ambiguous, it probes the query route before deciding between
"unknown drawing" and "server too old". Conflicts raise
DrawingLibraryConflictException carrying the current record, which the save
flow needs to offer a reload or the next revision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:03 -04:00
ajandClaude Opus 5.5 3c577d4ac7 feat(io): drawing package
Library drawings travel as a single-drawing, zero-plate .nest archive so the
server stores opaque bytes and the client reuses NestWriter/NestReader. A
characterization test first proves that shape round-trips the program, hole
programs, source entities and suppression, offset, material, bends and units.

Packages are canonical: fixed archive and nest timestamps, zero nest quantity
and no best-fit caches. The same drawing content therefore has the same bytes
and SHA-256, which later revision checks rely on to tell geometry changes from
detail-only edits. Read checks the archive's own declaration before the lenient
nest reader, which would otherwise default unknown units and drop empty plates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:02 -04:00