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>
Nine server test classes opened the test database with their own
identical Execute and Scalar helpers. They now import TestDatabase.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Auto Nest leaves out ledger item codes already on other open server nests,
and save and post re-check a nest's codes before going on.
- Server: GET /api/nests/stock-item-codes?exclude= lists the codes on
non-deleted Draft, Quote and To Be Cut nests. Codes are stored in
nest_stock_codes (schema v14) at every archive write; the upgrade reads
each current archive once.
- Data: INestRepository.GetStockCodesInUseAsync, LedgerCodesInUse (5 s,
failures become an unknown answer), the pure LedgerCodeFilter for ledger
rows (reusable by release-time nesting) and LedgerCodeCheck for the
re-check.
- Desktop: the Auto Nest ledger summary counts "(N on other nests)" or says
other nests were not checked. Save, Save As and post open Ledger Stock
Changed when codes are taken: proceed after a per-attempt
acknowledgement, remove the codes (undoable), or cancel. A ledger that
cannot be read skips the check with a status note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Schema v13 adds shared_settings and shared_setting_revisions behind
/api/settings: one revision-guarded JSON document per kind and key, with
every revision (deletions included) kept with who saved it. Recreating a
deleted document continues its revision count.
OpenNest.Data gains the client (RemoteSharedSettings), a synchronous store
that keeps the last copy read for offline use and refuses writes while the
server is unreachable (SharedSettingsStore), a machine provider over it, and
JSON parse/serialize entry points for clamp settings and nest defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A record's thickness is in the nest's units, but nothing on the record said
which, so a list of nests could not tell 9.525 mm from 9.525 inches without
reading each archive. Records now carry units, read from the uploaded
archive like the counts (absent or unknown values are inches) and kept by a
metadata-only save. Schema v12 adds the column and reads every stored
archive, current and historical, so existing millimeter nests report it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operators want to release a few loose drawings without first building a
set for them. The release record gains a `drawings` list (drawingId,
quantity, joined drawingName) and nest items with no `setId` carry those
drawings' quantities, so grouping, merging and undo stay one path.
Schema v11 adds release_drawings and rebuilds release_nest_items with a
nullable setId (SQLite cannot drop NOT NULL), keeping every row. The
query gains `drawingId=`; a server from before this refuses that unknown
parameter with 400, which the client turns into ServerTooOldException.
SetReleaseSession asks before building anything, because the release is
posted only after its nests are saved and an old server would refuse it
then.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nests, drawings and releases store material and grade as free text, so
there was no canonical list for clients to suggest from or to fix
spelling against. Schema v10 adds materials and material_grades keyed by
SharedListNames.Key, with GET/POST /api/materials. Adding is idempotent
by key and keeps the first spelling.
The tables start empty: the materials already stored did not import
cleanly, so the list is filled through the API instead. Existing
free-text columns are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A release can now cover several sets, each with its own kit count, so parts
from different kits share sheets. It can also merge into an existing draft
nest instead of always creating one. Schema v9 replaces the single-set
release tables: releases, their set lines, their nests (created or merged,
with the nest revision before and after) and the drawing quantities each set
added to each nest, so a kit stays traceable once kits share a nest. The v8
migration turns each existing release into one set line with created nests.
Undo runs on the server in one transaction: created nests are soft-deleted
and merged nests restored to their pre-merge revision, but only while each
still holds what the release wrote. Otherwise nothing changes and the
changed nests are listed, so the operator can skip them. A nest restored to
that revision counts as unchanged, which lets releases sharing a nest be
undone newest first.
The old /api/sets/{id}/releases and /api/nests/{id}/release routes are
replaced by /api/releases; no tagged desktop used them. The desktop still
releases one set at a time until the release dialog is reworked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nest records gain a materialGrade field (server schema v8, blank for existing
rows) that is searched, sorted and versioned like the other metadata. The
Nest Info dialog gets a grade picker listing the machine configs' grades for
the chosen material; density survives only an unchanged name and grade. A
blank recorded grade keeps the archive's grade while the name matches, so
records saved before v8 do not lose it on open. Saved Nests shows a Grade column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Releasing a drawing set makes one nest per material group, so a kit run
spans several nests. The server now records which set revision was
released, the multiplier and the nests it made, so a run can be tracked
across its nests and a nest can say where it came from.
The client uploads every nest first and posts the release last: a failure
part-way leaves ordinary nests and no release, never a release pointing at
missing nests. For the same reason a release is accepted for an older set
revision or an archived set, since the nests already exist by then. The
release id is client-allocated and a nest belongs to at most one release,
so a retried post gets 409 with the release that already holds it.
Releases are append-only and read joined to each nest's current metadata,
so status and deletion are always current. The tables join schema v7, as
the set tables have not shipped yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A drawing set is a named list of library drawings with quantities (a kit,
an assembly, a recurring order) that will later be released into one nest
per material. This is the server half: sets get the same revision,
If-Match and archive contract as /api/drawings so the client and tests
follow a known shape.
Schema v7 adds drawing_sets, drawing_set_revisions and
drawing_set_members. Members reference drawings by id and mean each
drawing's current revision, so reads join the current drawing record:
the client can group members by material without one request per drawing,
and the joined record is ignored on save so a read record can be sent
back unchanged. Every change keeps its own member list, and
/api/drawings/{id}/sets answers "used in" from current revisions only.
The drawing query's enum, integer and history-paging parsing is now shared
so both APIs refuse bad input identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>