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>
Every Auto Nest run, and every Default-engine run, recomputed each part's
best-fit pairs: job snapshots are fresh objects and BestFitCache keyed
its entries on Drawing reference, so the Irregular engine paid
PairCatalog.Build (about half of a 118-part solve) on every run of the
same drawings.
BestFitCache now keeps a process-wide, bounded (512 sets, least recently
used first) pool of unfiltered candidate sets keyed by the exact content
of a PartGeometrySnapshot (mode and every motion value, bit for bit;
PartGeometrySnapshot.ContentComparer) and the spacing. Every drawing
DrawingJobMapper.CreateDrawing reconstructs from a snapshot declares
that geometry (BestFitCache.ShareByGeometry), so the Irregular pairs and
blocks, the cutout lattice and the Fill/Default job paths all read and
fill the pool. Candidates depend on the program geometry and spacing
alone, so a pooled set is the set the search would produce: benchmark
layouts, costs and placements are unchanged on the synthetic nests (all
engines) and on the 67-drawing, 118-part job. Failed searches are not
pooled, Invalidate leaves the pool alone, Clear empties it, and a
drawing whose Program is replaced stops vouching for its geometry.
Same 118-part job solved twice in one benchmark process (--parallel 1):
the second solve now takes 13.1 s after a 33.0 s first; before, 35.6 s
after 41.8 s.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.
Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).
Formatting is now enforceable: dotnet csharpier check . passes.