kerfmaster — the pipeline
The same 299 pins, on the axis the software is actually built along · updated 2026-09-21
By subjectBy pipeline stageBy evidenceOpen questions
▶ A pin says what we decided. Its contract says what the stage takes, what it makes, and what makes it fail there. Once those names line up, a gap stops being something we notice by luck: everything under Holes below was computed from the contracts, not spotted by eye.
⚠ Unknown artifact name(s): contours, def-input-formats, dxf, healing, nc-audit, nest, rate-table, shop-data, shop-dxf, start-points, vector — not in the vocabulary, so any hole they cause is a spelling mistake, not a finding.
Holes — what a stage needs that nothing upstream makes
absentdef-input-formats — nothing produces it. Needed at: ?. Produced by: nobody. No pin anywhere claims to produce this. Either a stage is missing from the list, or the name is wrong.
absenthealing — nothing produces it. Needed at: ?. Produced by: nobody. No pin anywhere claims to produce this. Either a stage is missing from the list, or the name is wrong.
absentshop-data — nothing produces it. Needed at: Intake. Produced by: nobody. No pin anywhere claims to produce this. Either a stage is missing from the list, or the name is wrong.
absentstack-up — nothing produces it. Needed at: Bridges. Produced by: nobody. No pin anywhere claims to produce this. Either a stage is missing from the list, or the name is wrong.
absentstart-points — nothing produces it. Needed at: ?. Produced by: nobody. No pin anywhere claims to produce this. Either a stage is missing from the list, or the name is wrong.
absentvector — nothing produces it. Needed at: ?, Intake. Produced by: nobody. No pin anywhere claims to produce this. Either a stage is missing from the list, or the name is wrong.
unrecordedcut-order — consumed by a record pin, produced by no record pin. Needed at: Cut order, Post. Produced by: rapid-over-slug (ruling). A pin describing what the code DOES depends on this, and nothing typed `record` makes it. Either the code does not produce it, or it does and this list never said so — both are defects of the list, and it cannot tell you which.
unrecordedtrue-size — consumed by a record pin, produced by no record pin. Needed at: Intake. Produced by: spec-checks (ruling), scale-gate (ruling). A pin describing what the code DOES depends on this, and nothing typed `record` makes it. Either the code does not produce it, or it does and this list never said so — both are defects of the list, and it cannot tell you which.
plannedbridged-part — planned both ends. Needed at: Nest. Produced by: bridges (intent), assembly-connectivity (intent), concealed-envelope (intent). No record pin on either side — a gap in the plan, not in the code. Fine now; a hole the day it ships.
plannedidentified-file — planned both ends. Needed at: Scale & units. Produced by: customer-portal (ruling), preflight-gate (ruling), delivery-rules (ruling), preflight-checks (intent), uploader (intent). No record pin on either side — a gap in the plan, not in the code. Fine now; a hole the day it ships.
plannedsheet-layout — planned both ends. Needed at: Nest, Report. Produced by: nesting (intent), tabs-nest (ruling). No record pin on either side — a gap in the plan, not in the code. Fine now; a hole the day it ships.
Intake
What arrives, whether we can read it, and whether it is allowed through the gate.
18 pins — 5 intent · 1 question · 5 record · 7 ruling
takescustomer-fileshop-data ?true-sizevector ?
makescontours ?healed-geometryidentified-filerate-table ?raw-geometry
- •Layer recordmixed — A named grouping of entities in a DXF — how we separate cut geometry from mark-up, and the original from our work.
- •Quote mode ruling — Sales' stripped surface: upload → material → auto-nest → estimates → pricing. It moves the start, not the program.
- 2PDF → G-code intentmixed
fails if A Bézier flattened into line segments; a PDF clip path read as cut geometry. - 2a.ai / SVG / PDF vector input recordmixed
fails if A Bézier flattened into line segments; a fit demanded tighter than the format itself carries; a tangent taken from the chord at a corner handle. - 2cThe vector front-end — built, measured, and where it resumes recordd10
fails if read() proceeding without a stated real-world size (it raises ScaleUnknown, deliberately); a fit-induced kink; deviation quoted without splitting out the format's own inherent error. - 7dSales surface — stripped intake/quote mode; jobs may originate in sales rulingreads as fact
fails if Building a separate quoting product; hardcoding any fee or estimate constant; letting quote nests bypass the production gates when promoted. - 7gCustomer self-serve surface — upload, educate, heal, approve, quote rulingmixedreads as fact
fails if Letting a customer approval bypass the operator, or asking the customer to approve anything sacrificial (tabs, leads, cut order) — they approve pricing and bridges, nothing else. - 7hShop-rate entry is an INTERFACE, not a spreadsheet — and the product direction behind it rulingmixed
fails if A rate value saved without who supplied it and on what authority; a 'measured' claim with no how; reads left open (this is the shop's pricing). - 8Cut vs. engrave rule questionmixed
fails if Engrave geometry cut clean through, or cut geometry engraved — with no rule held, either is currently possible. - 12Sub-kerf degenerate-speck drop intentmixed
fails if A feature above kerf width dropped as a speck; a sub-kerf speck left in to distort the fidelity score. - •DWG input support record
fails if A .dwg that converts silently wrong — conversion is a step, not a read. - 14Preflight — the intake gate rulingmixedd10deferred d14
fails if A file entering preprocessing that failed a check, or passed one we never ran. - 14aHow to deliver files to us ruling
fails if A file converted by the customer before it reached us, accepted as if native. - 14cThe check list — what is fully automated, what needs a human intentmixed
fails if A question of intent answered by automation; a check listed as automated that a human actually does. - 14dThe uploader — how the automation would work intent
fails if A report card that does not end in one clear question or a release. - 14eThe education we give customers intentmixed
fails if A rejection message that does not teach what to do differently. - 14fWhat our DXF reader actually sees — and what it silently does not rulingmixeddeferred d14
fails if An entity type dropped without a word to anyone. - •Reading an .ai by named layer — mvt_layers.py record
fails if Quoting a part size that is really the artboard clip; reading a layered .ai as one flat drawing.
Scale & units
Turning file numbers into real-world size. A guess here is wrong by a factor.
2 pins — 2 ruling
takesidentified-fileraw-geometry
makestrue-size
Heal
Facets and Béziers into true lines and arcs, faithful to the customer's actual shape.
9 pins — 4 record · 5 ruling
takesraw-geometry
makeshealed-geometry
- •Healing rules (dirty CAD → clean arcs) rulingmixedd11
fails if A fit-induced jog anywhere; deviation over the tolerance gate measured against the TRUE source; a native bulge re-fitted; a rule that only works on one file.; a sub-tolerance smoothing pass keyed to feature size rather than to deviation; a smoothing allowance carried as a separate number from the measured end-to-end deviation; a corner rounded when the feature it sits on is NOT inside the operator's tolerance.; one tolerance applied to a whole file where the features have different functions; a default gate defended on a feature whose purpose does not need it. - •Fidelity record — How closely our healed output matches the customer’s TRUE shape — the ‘did we change the part?’ number.
- •Jog / kink ruling — A non-tangent break where the true shape is smooth; a fit-induced one is an automatic FAIL.
- •Spike vs. sustained recordmixedd11 — How the healer tells a real corner (keep sharp) from a coarse arc (smooth it).
- •Bulge ruling — The DXF encoding of a native arc; we preserve these exactly, never re-fit them.
- •Polylining ruling — Exploding a real curve into hundreds of tiny straight lines — the damage we exist to undo.
- •Healing / healed recordmixed — Healing is the process; healed is the result — the clean geometry every downstream stage works from.
- •Tolerance gate vs. fit target record — The tol argument is an accept/reject gate — not a fit goal; we always best-fit tighter.
- •Repair mode must compare the two sides of a mirror split rulingmixed — Jordan 2026-08-10: repair mode must compare left and right of the split. BUILT: repair.py symmetrize() rebuilds meant-to-be-symmetric contours as left half + mirror; kern() does operator-directed letter respacing. Build 022.
QC
What the geometry IS: closed or open, inside or outside, island or hole, ours or the customer's.
7 pins — 2 intent · 1 question · 3 record · 1 ruling
takesraw-geometry
makesdeliveryhealed-geometrytopology
- •Island recordclosed d16 — Kept metal fully surrounded by cut-away area; drops out unless held by bridges.
- •Undersized ruling — Narrower than the table minimum for that material and thickness — a flag for the operator, never a silent re-cut.
- 9Auto-close threshold questionmixeddeferred d14
fails if A gap snapped shut that a human should have seen; a gap flagged that every file has. - •QC step 2 — QC_FIX correction layer intentmixed
fails if A correction applied silently rather than as a separate, labelled, deletable layer. - •QC step 3 — delivery intentmixed
fails if Fixes counted on the banner that are not visible on the board. - •QC step 1 — detection record
fails if An open contour or duplicate segment that does not reach the board gate. - 13Source-defect flag — steps that are in the customer's file recordmixed
fails if A defect that is in the customer's own file reported as ours, or silently healed away.
Bridges
Permanent holds so islands stay in the finished part.
4 pins — 3 intent · 1 ruling
takesmaterial-tablestack-uptopology
makesbridged-part
Starts & leads
Where each contour begins, where the pierce lands, and how much clear air it has.
4 pins — 2 intent · 1 record · 1 ruling
takeshealed-geometrymachine-profilematerial-tabletopology
makesboundaries-with-startsoperator-choices
- •Lead-in / lead-out rulingclosed d18 — The short ramp on/off the cut path where the pierce lives; never inside a tab span.
- 4Lead-in/out + cut-order optimization intentmixed
fails if A lead crossing geometry; a pierce whose clear air is under the standoff and is not reported; comp side inferred from geometry rather than declared. - 4bOperator pick surface — per-boundary overrides intentmixed
fails if A per-boundary choice made silently by the tool where the record says the operator picks. - 4dStart points & lead geometry recordmixedd3d5d6d7closed d18
fails if A start at a sharp point of the design; a start chosen before its lead, so the boundary ends up pierced with no lead; a corner inside the tab span, unreported.
Cut order
The time-ordering. This is a safety stage, not an optimisation.
2 pins — 1 record · 1 ruling
takesboundaries-with-startstopology
makescut-order
Nest
Parts onto a sheet, and the sacrificial tabs that hold them there.
8 pins — 1 intent · 7 ruling
takesbridged-partmaterial-table
makessheet-layout
- •Tab ruling — A deliberately breakable connection — holds parts in the sheet while cutting, snapped off afterwards.
- •Wirejoint (MetaCam term) ruling — MetaCam's word for the sacrificial micro-joint — what we call a tab. Confirmed by Jordan, 2026-08-28.
- •Nesting ruling — Packing multiple parts on a stock sheet for best yield; must honour the bed envelope. Two kinds with opposite pipeline orders: true nesting and part-by-part nesting (Jordan, 2026-08-27).
- •True nesting ruling — The SAME part duplicated over and over on the sheet (Jordan, 2026-08-27). The toolpath is established FIRST on the single part; the nest then duplicates one short piece of G-code per placement.
- •Part-by-part nesting ruling — Several UNIQUE shapes nested on one sheet (Jordan's shop term for the files: Torongo files). The nest is laid out FIRST; the toolpath is established after, on the assembled sheet.
- •Bounding box ruling — The smallest upright rectangle around a part — a quick estimate only. Nest gaps are NEVER measured between bounding boxes, always between actual part geometry (Jordan, 2026-08-26).
- 3Nesting intentmixed
fails if A layout over the 80″×160″ bed; parts overlapping; yield reported without the sheet it was measured on. - 3aTabs — nest-time hold & break-off rulingmixed
fails if A tab that will not snap by hand; a lead or pierce inside a tab span; a width used that is not in the material table.
Post
Fanuc macro-B out, including the slug-retention tabs.
8 pins — 1 question · 2 record · 5 ruling
takescut-ordermaterial-tabletopology
makesgcodemachine-profilenc-audit ?
- •Output & G-code rules ruling
fails if Anything emitted that is not G1/G2/G3; a curve posted as segments; a native arc re-fitted; the G52 margin baked into coordinates instead of applied at post. - •Slug-retention tab recordmixed — A third kind of hold: material left uncut so an interior drop-out can't fall through. Width now production-validated — see item 3b.
- •Fanuc macro-B ruling — Our production G-code dialect: O-number program, #-vars, G41/42 comp, M98 pierce, true arcs.
- •Subprogram rulingclosed d13 — Jordan, 2026-08-04: one part's G-code, made from a DXF, called repeatedly across a nest.
- 3bSlug-retention tab rules — production-validated rulingmixed
fails if A slug over the drop-through size emitted with no tab; a tab narrower than the table for that material; a job carrying another machine's drop-through; a posted job with no numbered slug report. - 4aPost dialect — match the shop's G-code exactly recordclosed d13
fails if A condition var outside #102–#111/#121/#122; the G52 margin missing; a difference from the shop's dialect that is not written down. - •M-code glossary — RECEIVED, and Jordan has ruled on every code questionmixedclosed d13
fails if An M-code emitted whose meaning we inferred rather than confirmed. - 4d-3Beam compensation column + micro-segment cleanup ruling
fails if A compensated block shorter than the material's beam comp value in anything we post; a micro-segment surviving into G-code; a material row posting without a beam_comp value.
Report
What a human is given: boards, sheets, slug reports, quotes.
16 pins — 7 intent · 1 question · 6 record · 2 ruling
takesgcodehealed-geometrymachine-profilesheet-layouttopology
makesdeliverymaterial-table
Cross-cutting
Rules that hold at every stage — they are not a step in the pipeline, they are conditions on all of them.
25 pins — 1 intent · 1 question · 5 record · 18 ruling
- •Mission & the pipeline recordmixed
fails if Any stage's own gate fails, or a stage is skipped without saying so. - •Working discipline ruling
fails if A claim stated without being verified; work committed but not pushed off-box; a secret in the code; a call of Jordan's parked for Aristide to re-confirm. - •Evidence discipline — controls, and the scope of a claim rulingclosed d15
fails if A control that shares a code path with the thing it checks; a boolean that changes nothing reported as a pass; a claim stated wider than what was measured. - •Thou ruling — One thousandth of an inch (0.001″) — our working unit for tolerance and fidelity.
- •Kerf ruling — The width of material the beam removes (~7 thou); the floor for real detail.
- •Bed envelope rulingmixedd1d2closed d17 — The machine’s cuttable area: 80″ × 160″. We flag anything that exceeds it.
- •Workbench ruling — The docked ground state of the picked A+C interface — every station starts identical; ships first.
- •Graft ruling — The tear-off machinery under the Workbench — panels detach, float, cross monitors; snap back in one click.
- •Workspace ruling — A named, per-user saved panel arrangement (Adobe-style); the default workspace IS the Workbench.
- •Interrupt-and-return ruling — Pull a delivered job back in, change it, repost, and resume — losslessly, as a first-class workflow.
- •Gate vocabulary ruling — cadmaster's owned visual status language — colors, dots, glows, flags — taught once in the first-run tour.
- •Units as a skin ruling — Display/input units are a per-user skin; every field takes a unit suffix (3mm converts); inches stay the internal truth.
- 4cTable data format — agree once, use everywhere questionclosed d17closed d18
fails if A number that lives in prose instead of a table; two tables with two shapes; a value an operator cannot override; a lookup that answers with a neighbouring thickness instead of refusing. - 4gmachine.json and the material table — the numbers, and where each came from recordclosed d19
fails if A number typed into Python that belongs to a laser or a material; a lookup that answers from a neighbouring thickness; a value with no source beside it. - 4hEvery number carries how well we know it — and the register audits itself record
fails if A number in the code that the register never claimed; a register entry for a constant that no longer exists; a copy that no longer equals what it copies; an assumed number quoted to a customer as if it were measured. - 4iThe named corpus — every file we hold, run every time, with the reason beside each number recordd20d21
fails if A change to the healer, the reader or the poster landing with no corpus run beside it; an expectation promoted to `rule` with no reason written; a failing `rule` accepted with --record instead of answered. - 4jEvery open question says who answers it and what it is holding up record
fails if A question parked without naming who can answer it; a question written inside one pin while it silently blocks five; a reference to a pin, defect or number that no longer exists; an answer that arrives and is not written back. - 6aUI architecture & the pipeline model rulingmixedd11
fails if A stage that runs without an accept gate; a result with no provenance; the engine reachable only through the UI; a customer file leaving the box. - 6bThe interface is reverse-engineered FROM this list rulingclosed d19
fails if A screen designed before the stage it shows is defined; a pin's record content changed in a different commit from the code it describes. - 7Interface workflow intentmixed
fails if An operator step with no way back; a fix accepted without being shown. - 7aPaper design v1 — the document under walkthrough ruling
fails if Any interface code written before both partners sign off on the paper; any stage in the app that skips a gate the paper lists. - 7bInterface look & layout — inputs recorded, design gated on research rulingmixed
fails if Any mockup or interface code before the love/hate research is delivered and the panel-layout choice is made; treating the two panel proposals as decided. - 7eUnits — per-user display units, unit-suffix input everywhere, inches stay the internal truth ruling
fails if Letting a display-unit preference leak into stored geometry, table values or posted NC; a field that accepts only the session unit. - 7fFirst-run tour + help, manual and bug-report from anywhere (auto-capture to partners' server) rulingmixed
fails if Building the tour or bug pipeline before the auto-capture spec and server details arrive; a help/bug entry point that is only on some screens. - 11Best-practices defaults rulingmixed
fails if A default that cannot be overridden in the interface.
Unassigned
No stage declared. Deliberately visible.
165 pins — 12 intent · 3 question · 121 record · 29 ruling
- •Everything gets a pin — every artifact, draft, open item and decision lands on the pin list, same turn it is born ruling — Aristide, 2026-09-13 — ruling: 'make it a rule that all items like these are always tracked in the pin site.' Born from the security-page draft, which existed in docs/ for a day with no pin — a future session would never have found it. Nothing lives only in chat, memory, or a stray file again.
- •Nesting rules — Jordan's rulings (2026-08-10/12) ruling — Spacing floor .200 (ruled for TL-1016); 96" runs in X and long parts lie in X (slat-bridging bias, not a rule); flip-180 profile nesting; open profiles close at 0.020 by trim/dedupe, never gap-bridging; no common-line; material > tipping > time. Y-first columns now implemented (remnant = one full-height strip at far X); lead-ins planned in-nest, 0.300" between lead-hosting hypotenuses (0.150 lead + 0.150 pierce standoff); Aristide 2026-08-13: separate orders, nested per ORDER — efficiency within an order, never across. X-extent is the cost function (Jordan 2026-08-18): fill internal voids before extending the X frontier; remnant = full-height strip beyond max X.
- •Production quantities in the nest — qty replication after heal, before packing record — split_parts.main(qty=N) replicates every grouped part N times AFTER heal/group/lead-planning and BEFORE the packer — the expensive per-part work runs once per unique part, copies are packed as first-class parts (ids p00XcNN). Built for the ISV-H grille job (Jordan 2026-08-21: 15 each on 48x96); first use = build 035 (105 parts, 5 sheets, min gap 0.2019, all gates pass). Composer for multi-file jobs: nest_isvh.py pattern — one copy of each healed part on a virtual canvas, qty at the nest call. Build 124 (2026-08-24) supersedes 035: Jordan confirmed MSO7,0.080 with 0.030 part tabs (his number equals the table value measured from his own 33199.NC) and sent a MANUAL flat for BNM78880 (build 123, PASS 0.62 thou) -- 8 parts, 120 placed, 5 sheets, min gap 0.2019, all nest gates pass.
- •Manual nest adjustments — the operator's move is applied, then audited recordfixed d36 — Jordan 2026-08-18 (-86/-93 screenshots): 'I moved the parts that are blue... Make these changes on your end.' split_parts.main(moves=) reads part -> outline-bbox target JSON, applies AFTER the packer, BEFORE the clearance audit -- a hand placement is measured like any other (first two targets failed at 0.1815/0.1967 and were corrected from the audit, not by eye). Recorded in stats.manual_moves. Same change: nest margin is a parameter; -86 nests at 0 because the baked 0.5 double-counted the G52X.2Y.2 the post applies (margin is per-job, applied at post, never baked into the DXF). Physical edge = the shop's own 0.2.
- •Program numbers & mixed-sheet traceability — deferred to 2.x ruling — Operator assigns the program number from the shop's list at nest time; auto-numbering and part-to-order sorting are version 2.x.
- •Bridge emission — bridges cut into the geometry (Jordan's go, 2026-08-10) record — place() is SYMMETRY-FIRST (mirrored pairs about a detected axis); widths 125/80 by Jordan's ruling; straddled limbs OWN their segments (window fill barred, round 4); every contour stays closed; MetaCam shop DXF carries the bridges.
- •The slot-break rule — showing "on top" in a one-piece cut ruling — Buried edge drawn as a narrow gap broken by ligaments; break at interior ends, never at emergence points.
- •STEP (.stp) input — step_io.py reader: bent parts unfold, flat parts deliver direct (builds 139/140/151) record — record: step_io.py (pure-Python AP214 B-rep reader, 2026-08-26) + unfold.py give STEP two paths: a BENT part develops to a flat blank (neutral-axis unroll, BA = theta*(R + K*t), K=0.33 stated assumption -- builds 139/140, BNM78651), and an ALREADY-FLAT solid skips the unfold: the top Z-normal planar face's outer + hole loops convert EXACTLY (STEP carries true curves -- no healing). First flat delivery: build 151, 8VACUSEPRNG0001_FLAT (Jordan 2026-08-28), DXF-vs-source audit 0.00005 in; material answered 2026-08-28: 16 ga 304 stainless (.060 in), Rev2 row exists.
- •Nest shop ergonomics — uniform arrays, axis preference, remnant as objective (Jordan 2026-08-24) rulingmixed — ruling: three packer rules from Jordan's mark-up of the ISV-H sheet-1 image. (1) Identical parts nest in a REGULAR orientation pattern -- refined same day by Jordan ('Push back on the P9997 hard rule: items like this can be 180 rotated in pairs', with an image of hook-shaped parts interlocked head-to-tail): one shared orientation OR a consistent 180-degree pairing that interlocks; the ban is on stray one-off rotations within a part type ('one part on the bottom right is randomly rotated 180 degrees from the rest. Human operators would not prefer this, but also using nesting macros like P9997 would not work well with anomalies like this'). (2) Odd tilts are suspect: min-area rotation stood two bracket types at 172.5/352.5 (7.5 deg off axis) and 'doesn't seem efficient' -- axis-aligned unless tilt buys real packing. (3) Remnant shape is an OBJECTIVE: 'why didn't you put these elements on top of the large grills on the left side, and leave the rest of the sheet for a cut off?' -- consolidate parts to one end, fill grille-sheet spare bands before sprawling, leave one clean rectangular cutoff.
- •Production nests: as few NC programs as possible — identical sheets = one nest + 'Run (X) copies' (Jordan 2026-08-25) rulingmixed — ruling: for large sheet-quantity jobs, identical sheet layouts collapse into ONE nest/program carrying a run-copies note — 'For nesting large sheet quantities like this, it's better to use as few nc programs as possible. Seems like some of these nests are similar, if not the same, and should be one nest/program with a note: Run (X) copies.'
- •Engineering-drawing PDFs read end-to-end — part extracted from dims/border/views clutter (2026-08-25, Jordan's 8ARCHBASSTL0006 test) recordmixed — record: vec_io now unpacks PDF 1.5+ object streams (CAD exporters pack the page tree there; the file read as zero paths before), and drawing_extract.py selects PART geometry out of a dimensioned drawing: fills+arrowhead twins dropped, stitched chains must CLOSE (dim/leader lines dangle by construction), page frame and loops outside the main view dropped, line-side junction snapping heals the drawing's own micro-gaps. 8ARCHBASSTL0006: all 16 printed dims within the drawing's +/-.005 block; our fit adds 0.27 thou, 0 kinks. Build 134: part snapped to printed callouts by Jordan's ruling (as-drawn scatter re-measured by Jordan in CAD). NX dialect (12729704): gapped/overshooting outlines bridged by angular pairing + trim + seam-spur cleanup (drop_seam_spurs); the two 'unresolved holes' were ONE 10.50x12.50 obround slot (datum C); scale proved by least-squares over all 14 printed spans (0.4703725 mm/pt); build 135 delivered in MM, 23/23 callouts exact from the delivered DXF. Build 156 (EXM275FG-0625): third dialect (Exotic, 0 beziers), 4.50 callout disambiguated from its own arrowhead positions, nominal-from-callouts nested 81-up on 36x48 .125 copper — first copper job and first production nest sourced from a drawing PDF. Fourth dialect (Navistar 3683209C2, build 160): doubled-run fragments deduped+restitched, true beziers via bezfit; part geometry now BUILT FROM CALLOUTS per the pdf-dims-govern ruling (holes 3/4 at stack ys 123/151, R6 lobes concentric, R16.4 waists re-solved by tangency) with the healed trace as the verification layer — max delta 0.333mm, all inside +-0.8. Supersedes build 159's drawn-position call.
- •Called-out dimensions are AUTHORITATIVE at their stated precision (Jordan, 2026-08-25) ruling — ruling: any dimension called out on a print must be exact in the DXF we deliver. The ONE legitimate mismatch is print-side rounding: a .375 feature commonly prints as .38 when the tol key gives +/-.005 for 3 decimals and +/-.010 for 2 — the decimal count SELECTS the tolerance, and a 2-place callout is a rounded view of the true value, not a command to build .380.
- •Machine condition tables — Jordan's Rev2 workbook is the MASTER (Jordan 2026-08-24) rulingmixed — ruling: 'Use these as our master but each of our clients may want to change these for themselves.' One sheet per machine (clients build one per THEIR machines too, 'organized in form-type so that our clients can modify these easily'). Rev2 supersedes prior measured/ruled values going forward ('You can refer to this table going forward'), including 1/8-and-under mild offsets (.004 vs the earlier .007) and the per-class D registers -- 'The new D values will be our improvement versus MetaCam's old ways of doing things' (Profile D3/#108, Inside D1/#107, Small D2/#106 -- corpus single-D7 is MetaCam's old way). Time columns (feedrate, pierce time, extra lead-in, corner dwell -- both in seconds, from the laser's own condition-table parameters) are ESTIMATE-ONLY: 'they don't have any effect on how GCode is posted'; pierce time 0 on thin aluminum 'is real'. Slug tabs: one width between Small and Large limits; 'The large limit for slug tab will replace that [0.75in drop-through] rule'. Small Threshold = SMALLEST dimension, inside contours only (a .100x.500 slot in .040 steel takes the small parameters). Bed sizes are PER MACHINE: GX 10K and EX 4.5K are 60x120; '80x160 is another machine which we have not added yet'. Stock (sheet sizes, cost) is 'a separate sheet and a separate table. Has nothing to do with the machine.' ZHL = focus type at the machine, irrelevant to posting. Etch: condition + feedrate columns still to be added (Jordan: 'remind me to do that'); etch runs as its own step right after cleanup in the interface, G-code adjusted accordingly. CONFIRMED 2026-08-31 (Jordan, on the post-wiring plan): 'What we want is the newest format, where D1/#107, etc. are defined by the tables' — registers and conditions are READ FROM the table rows, never hardcoded; a client's own machine sheet defines their registers. WIRED 2026-08-31: registers ingested from the offset headers (profile D3 / inside D1 / small D2, per machine); tables.rev2_tooling raises on blanks; write_gcode_macrob(tooling=...) classifies profile/inside/small per boundary and posts each class's #501 condition + D register (legacy path untouched); nc_audit(tooling=...) independently re-derives class from posted geometry and flags mismatches (legacy control: 3/3 caught); bed 60x120 in machine.json; MSO7,0.375 row added (.016 decided from Rev2). ETCH VARIABLE HOME 2026-09-01 (Aristide: don't wait for the numbers, 'you already know it is going to be a variable, just work around that'): tables.etch(material, machine) exists now -- reads etch_condition/etch_feedrate off the Rev2 row when typed in, raises MaterialIncomplete while absent (TBD never a silent default, no etch literal in Python); Jordan's columns become a row edit, not a build. Open: etch column VALUES (Jordan owes), per-machine profiles, rotated-slot min-width.
- •Interface requirement — right-click per-contour override of any table value (Jordan 2026-08-24) rulingreads as fact — ruling on future interface, NOT build authorization: 'In the final interface, the software should give us the option to right click and override whatever value came from this table for that particular contour. When they right click, it should display the \"current\" value.' MetaCam's tooling dialogs (EDIT_TOOLING_0-3) are the reference feature set -- 'it's ugly but functional. We will look for these features to be added to our interface.' Sharpens the standing best-practices rule (every default overridable) into a mechanism: per-CONTOUR override, seeded with the value the machine table resolved for that contour so the operator sees what they are overriding. Scope per MetaCam's dialogs: condition number, direction, comp on/off (No Beam-offset), constant power, approach (length/radius/angle/pierce/dwell/condition), escape (same + gap width, after-cut). Extended same day (Jordan): lead-in LENGTH and PIERCE TYPE are also per-contour overridable, and the alternative pierces he will add to the table 'will be available to override easily for the operator.'
- •Scanned/raster input — the 3A76 lessons (ratified by Aristide 2026-08-27) ruling — A scan trace is a reviewable DRAFT, never production geometry. Ask for the customer's native CAD first. And an audit must be independent of the pipeline it audits — comparing a trace to its own re-run confirmed defects as 'matches'.
- •Input triage prompt — ask the right questions when a file predicts a poor result (Aristide 2026-08-27) intent — Intent, phase rule applies: when load-time analysis sees the input cannot give a good result (raster scan, no scale, fused leaders, unreadable callouts), the interface prompts the operator with the RIGHT QUESTIONS instead of proceeding silently. Aristide: 'ask the right questions, if you see that the file is not going to give a good result, a pop up prompt?'
- •Toolpath view — visualize the posted path on screen recordmixed — intent (Jordan, 2026-08-28): the interface shows the path the laser will follow — cut direction, pierce points, lead-ins, and dashed rapids feature to feature. MetaCam's tooled view is the reference example. RAPIDS LAYER BUILT 2026-09-17 (Jordan: 'a good feature for visual reference for the operator ... make it hideable like the other features on the left column'): rapids between features are a toggleable display LAYER (toolbar button + key R, default on, rides the Colours & legend --dim role). Three scenes: (1) the Part review scene draws the chain straight from review.json's plan array -- the FLOWN approach (Traverse Routing waypoints) when the row carries one, straight line otherwise, arrowhead at each landing pierce; no cut-plan judgment needed, a Manual Sort re-plan re-orders it by itself; (2) the Cut plan scene's existing rapids now obey the same toggle; (3) the part-list Nest canvas chains the whole sheet in the CURRENT emission order (the numbered boxes' own numbering, plOrderForSheet), features within a part in plan order, straight between parts (what the part-by-part main program does); _pl_build plan rows now ship the within-part flown rapid (base frame, rotated like pierce/segs) so a NEW pack draws routed approaches -- packs made before this build fall back to the straight line, honest, a re-pack refreshes. Chain restarts per sheet (each sheet its own program). Honest limit: the single-part TRUE-nest canvas (Nest tab on a single job) still draws leads only, no sheet chain. Display only -- no engine or judgment change. STITCHED 2026-09-18 (Jordan's Rapid_Path screenshot -- 'Rapid path does not represent reality of the nest?'; Aristide 'Go'): the part-list chain drew each part as its own ISLAND -- the pack ships each part's lone-plan approach whose first point is the part's OWN origin, and the canvas trusted it at the seam, so every entry leg rose from the part's lower-left corner and no leg connected part to part. Now each placement's entry leg is stitched to where the head really is: the previous part's last cut end (routed waypoints kept), the sheet's first part from the G92X0Y0 program start. Within-part rapids unchanged; client-only (app.js). Verified on the dev CAMP nest -- one continuous chain in emission order. RAPID DIRECTION ARROWS 2026-09-18 (Jordan's Rapids_9-18-1 BRONX screenshot -- 'One of the things that would really help the operator is to clearly see what direction the rapids are heading. What I'm expecting is a repeated arrow along the rapids' -- plus his MetaCam reference shot, whose lone tiny end-arrow is the thing to beat): every rapid leg on all three drawing surfaces (review-scene plan chain, cut-plan scene, part-list Nest canvas) now carries REPEATED arrowheads oriented along the direction of travel -- one shared helper rapidArrows(), arrows every ~64 SCREEN px (zoom-independent: wherever the operator looks at a rapid there is an arrow nearby; a short leg keeps just its landing arrowhead, which stays on every leg). Same --dim colour and layer as the dashes, so the R toggle hides arrows with the chain. Client-only (app.js). Verified live on dev (B7787 review chain + cut plan screenshots -- direction reads at a glance across crossing legs) + verify_traverse 10/10; traverse check 10 made self-sufficient in the same pass (a verify_draghandle run re-plans nav209 and drops cutplan.json -- suite-order coupling, the check now posts its own cutplan door first; suite fix, zero app change).
- •Static toolpath plot — every posted program ships one record — record: toolpath_plot.py draws the laser's path from the posted NC — green cut path with direction arrows, orange lead-ins, numbered red pierces, dashed rapids in cut order. Wired into post_nest; approved by Jordan 2026-08-28.
- •Per-contour tooling document — the override slots get their data-model home record — record: tooling_doc.py + emit stats["contours"] — every posted boundary's decisions (sequence, condition/register, comp, direction, start, pierce, lead, tab) recorded as ran-value + source, with an override slot per ruled field. CONSUMABLE = start + lead_length; post_nest ships the document beside every NC and takes overrides= back in. The seam the right-click UI will sit on.
- •Drag-handle validator: click-time findings for a forced start/lead (built 2026-09-01) record — 3d, built on Aristide's 'Build what is buildable now' (2026-09-01). rules.check_drag(prims, overrides, material, tooling=..., **post_kwargs): hand it the part and a proposed per-feature override (the drag -- write_gcode_macrob's overrides= shape, or tooling_doc.overrides_from of an edited doc) and it answers 'if the handle drops HERE, these findings fire' -- before any NC exists. No drift by construction: the plan comes from the ENGINE's own planning (a plan-only post to os.devnull with the new emit plan_samples=True seam -- lead aim, scrap-side, tab span and snap are exactly what a real post would do; the plan poly is the AS-CUT contour so the tab gap is visible to rules.annotate) and the judgment is the same shared rule functions the NC audit calls (rules.check_plan) at the audit's own parameter sourcing (tables drop-through / material standoff). Returns findings red-first plus 'dragged' (the bnd ids the override features bound to -- read back from the engine's own per-contour record, never re-derived) and 'dragged_findings' (the subset touching them) so the UI can show the operator what THEIR drag caused vs what was already flagged. Warn never block; the posted-NC re-read stays the final gate. Proven: test_drag.py 10 controls incl. the parity control -- the same overrides posted for real and audited report the identical (rule, bnds) findings. plan_samples is off by default: normal posts and tooling docs byte-unchanged (control 1).
- •MetaCam Add-Toolpath walkthrough — the operator-override reference set ruling — ruling/reference (Jordan, 2026-08-28): full screenshot walkthrough of how the shop tools a part in MetaCam — auto-tool seed, then detailed overrides: per-feature dialog (2A), drag lead-in/pierce + typed values (2B), manual sequencing (2C), all-identical vs single-feature propagation (2D). All open questions answered 2026-08-28: Alt/Shift keys = MetaCam's failed resequence shortcuts (our click-in-order model is the envisioned UI); MetaCam warns on risky re-sequence, dismissible (→ gcode-warning-callouts); wirejoint rides the approach drag. BUILD CONFIRMED 2026-08-31 (Jordan): the drag-handle engine ('this will definitely have to be built') and identical-feature propagation ALL-mode ('Good, build it') are ordered — engine pieces first (validator + fan-out), canvas later per phase. FIRST ENGINE SLOT BUILT 2026-08-31: emit.write_gcode_macrob(start_at={'outer': (x, y)}) forces a boundary's start/pierce to the contour point nearest the given one — tab span, snap and into-scrap lead rules all still apply. Born on O33286: the lone-part chooser aimed the outer lead -Y into the next nest row; the nest layout owns the pierce-channel knowledge, so it passes the point. The full per-contour override surface stays intent.
- •Warning severity + warnings carried into the posted G-code intent — intent (Jordan, 2026-08-28 — decided): risky operator choices (e.g. manual re-sequence) raise a warning with severity — yellow or red — the operator can dismiss; if G-code is generated with a live warning, the NC carries a (warning) comment at the spot AND a top-of-file callout: (Contains X # of warnings). DECIDED 2026-08-31 (Jordan): compute the warnings LIVE as well — one-time refactor so the live click-time check and the final NC audit call the SAME rule functions; the posted-NC re-read stays as the final independent gate (it judges what the machine will see). No downside beyond the refactor; single source of rules so the two views cannot drift. REFACTOR DONE 2026-09-01: rule functions shared — see pin click-time-rule-checks; NC CALLOUTS BUILT 2026-09-01 — see pin nc-warning-comments; severity colours still the proposed table awaiting Jordan.
- •Shared rule checks — rules.py, one set of functions for the audit and the click-time warning record — record: the decided one-time refactor (Jordan 2026-08-31) is DONE 2026-09-01 — the rule DECISIONS (comp side, tabs/traffic, cut order, pierce standoff) live in rules.py; nc_audit calls exactly these functions on the posted NC (final gate unchanged — reference audits byte-identical) and rules.check_plan() runs them on a PLANNED sequence with no NC file: the click-time entry point. Findings carry a PROPOSED yellow/red severity, awaiting Jordan's ratification.
- •Warnings ride IN the posted G-code — nc_warn.py writes Jordan's (warning) callouts record — record: built 2026-09-01 (Aristide 'let's do it') — every post_nest program that posts with yellow/red findings now carries Jordan's ruled callouts: (CONTAINS N WARNINGS) right under the O-number plus the numbered full messages, and a (WARNING k: SEV RULE) spot marker after each affected (BND:n). On a P9997 macro program the expanded-boundary findings map back to the subprogram's local bnd — one marker covers every arrayed instance. Comments only, proven transparent: the annotated file is re-audited and must report identical anomalies or the post raises. Warn, never block. EVERY POSTING SURFACE 2026-09-01 (Aristide 'Build what is buildable now'): the audit->inject->recheck sequence moved into nc_warn.audit_and_inject() -- the one call a posting surface makes after writing a program. post_nest refactored onto it (behaviour identical, test_nc_warn 12 controls pass); the board surfaces wired: build_board.py (_prod125.nc) and build_vec_board.py (_prod.nc) now audit every board-built production post and write the callouts in-file (they previously posted UNAUDITED -- closing that too). Proven on discarded smokes: ex2 prod post carries 6 warnings (2 red incl. wrong-side comp), EAGLES prod post 5 (red wrong-side comp bnds 17-19 + red untabbed-slug traffic) -- real findings on old fixtures, now travelling with the program. Bespoke deliver scripts call the same helper going forward. Still open: severity ratification (Jordan).
- •Job versioning — every customer job is a chain of openable versions intent — intent (Jordan, 2026-08-28 — decided): all files carry versioning; 'Envision GitHub as a model.' Every posted delivery automatically creates an immutable version; internal chain = what we produced (commits), customer print rev = label on a version (tag). Deprecation RULED same day: versions immutable forever; metadata marks — superseded (automatic on replacement) vs deprecated/do-not-cut (explicit human call with reason; re-posting = RED warning). Default open = latest delivered.
- •Common-line cutting — wanted, gated by a shop preference set at onboarding intent — intent (Jordan, 2026-08-28): common-line cutting functionality wanted; the modal's common-line block defaults COLLAPSED under a chevron for a precision shop and OPEN for a speed shop — default disclosure seeded by the onboarding archetype, never fully hidden. Reference: the common-line block of MetaCam's Layout Array dialog.
- •ONE SOURCE OF TRUTH, THREE LENSES — every UI feature exists on Web, Mac and PC (Aristide 2026-09-01) ruling — ruling (Aristide 2026-09-01, 'one big last rule for the UI'): "We need to have one source of truth, always. When we ask for a feature or button, it needs to be developed for all versions: Web or Mac or PC. So it needs to be looked at through all 3 lenses, always. Do not develop a feature for the web version that later can't be also created for the PC and vice versa. Same for Mac vs web." Two binding halves: (1) ONE SOURCE OF TRUTH -- a feature's logic, data and defaults live once, in the shared engine, never re-implemented per platform (the standing build rule 'data in JSON, generator separate from presentation' is this rule's foundation: engine decides, every shell renders); (2) the THREE-LENS GATE -- before any feature or button is accepted for one platform, it must be checked as buildable on all three (Web, Mac, PC); a design that only one platform can carry is rejected at design time, not discovered later. Applies from the FIRST UI work onward -- the phase rule still holds (record intent, build engine seams), and this ruling is why the engine-slot discipline (per-contour overrides, layout-modal one-clicks, drag validator) keeps every UI feature representable as data + engine calls that any of the three shells can drive.
- •Onboarding wizard — shop-level preferences asked once, up front intent — intent (Jordan, 2026-08-28, first mention): shop-level preference questions asked once at setup. Underlying axis (same day): two kinds of shops — precision-first (his) vs speed/efficiency-first — 'We need to be able to accommodate both'; the archetype seeds defaults (common-line the first case), never locks features.
- •Uneven +/- tolerance — default to the CENTER of the band (Jordan 2026-08-27) ruling — ruling: 'When a tolerance has an uneven + versus - let's make it a rule to go to the center of the tolerance. If the operator disagrees they will be able to manually change the hole size, but center of tolerance should be the default.' Where we generate or size geometry from a print callout, the default cut size is the mid-band value; a symmetric +/- keeps nominal (same number). Operator override rides the per-contour right-click slot. First applied: build 145 (LS1156258 holes o3.8 +0.08/-0.03 -> o.1506).
- •Nests fill Y FIRST — full sheet height, remnant one vertical slab (Jordan 2026-08-27) ruling — ruling: 'I see once again that you did not maximize the Y first. Why does this keep recurring? I thought it was a rule already.' Every nest stacks the sheet's full height before consuming X; leftover is one clean full-height vertical slab at max X. Max-Y is judged against the SHEET EDGE, not an assumed margin — his NC 28585 runs 32 rows to 47.845 of a 48in sheet; our 0.5 margin (assumed since build 137, never ruled) + the Rev2 0.25 aluminum gap is what cost the 32nd row. Numbers answered same day: margin DERIVES from the Rev2 min_gap column (see nest-margin-from-min-gap); builds 148/149 re-issued, rule wired into nest_rules.py.
- •Pocket backfill — a spill sheet empties into the true-outline gaps of earlier sheets (Jordan 2026-08-31) record — record: born on build 153 — Jordan: 'I know the 2 rows on sheet 4 could fit in the gaps on the other 3.' The row packer's monotone right-profile can never revisit the concave pockets between meshed long parts once a row closes, so spill opened a 4th sheet while inches-deep pockets sat empty. split_parts.pocket_backfill now runs after packing: last-sheet parts are placed into REAL free space on earlier sheets — occupancy-bitmap prefilter, then exact placed-outline clearance at the ruled gap and pierce standoff; audit_clearance stays the gate. All-or-nothing per sheet (a last sheet that cannot fully empty is restored as packed — partial moves buy no material and scramble a reviewed layout). Candidates are capped at each sheet's used X extent, so the clean remnant slab is never spent automatically (that spend stays the operator's click, per the spread-X ruling). Locked rotations honored. First applied: build 154 (ISV-H recall prototype, 4 sheets -> 3, util 50.6 -> 67.5, audited gap 0.2502). stats.pocket_backfilled counts the moves. Nest-in-hole ruled an operator option same day (Jordan): the engine may use big interior cutouts only as an operator toggle — intent recorded, UI not built.
- •Sheet-edge margin DERIVES from the Rev2 min_gap column — and the operator may shrink both (Jordan 2026-08-27) ruling — ruling: 'The 0.5 margin is not a rule, this should be derived from the "minimum gap" column of the tables we provided. In this case it should be set to 0.25 (.125" aluminum, cell 51 U).' Gap and edge margin are ONE table value; the operator may shrink them case by case (easy in the interface). His worked example on LS1156258: 0.23 instead of 0.25 — 0.020 tighter — fits 32 rows/col instead of 31, 12 columns instead of 13, saving over 5in of X. Both options delivered (builds 148/149) so the operator picks; applied to stainless same day — build 150 re-tiles the 3A76 lattice at margin 0.2 (was assumed 0.5): 300 pcs vs 288. In code: tables.nest_gap / tables.sheet_margin / nest_rules.resolve_spacing.
- •Free-rotation double-lattice nest search — the between-column meshing the shelf packer lacks (trial_lattice.py) record — record: angle sweep + 180-pair unit cell + slide-to-contact on TRUE outlines, tiled on lattice vectors; the pairwise fine-outline audit is the gate, never the constructor. Three production deliveries: build 147 (3A76, 36x60), build 157 (untooled) and build 158 (EXM275FG-0625 TOOLED FIRST per the true-nesting order — Jordan 2026-08-31 'add the real toolpath THEN nest': outer lead 0.15 arrayed with the part, common start point searched so every copy's pierce lands in a meshing void, verified exhaustively at 0.2745 clear-air vs the 0.15 standoff; still 99 on 36x48 .125 copper at the ruled 0.2 gap vs the shop's 94 at their measured 0.303 = lead+standoff across a straight channel — their NC 66261 layout, 0/180 half-offset rows, is the search's theta=0 case and it reproduces exactly 94 there). Benchmark 12729704: 114 vs their 108 (not delivered). Still a per-job script, not wired into split_parts — the standard path still advances columns by full bbox; recorded gap.
- •Two-orientation true nest = STRICT alternation, two P9997 macros (Jordan 2026-08-31 — ruling) ruling — ruling, on build 159's free-packed 125/65 mix: 'There are two orientations, which makes sense, but then they are not nested evenly, I would make every other part 180 degrees, and end up with two true nests, and therefore the main program would have two P9997 macros to make editing very easy. That would fall in line with what we've discussed before regarding nesting biases.' When a true nest runs two orientations, they alternate REGULARLY (every other part 180) so each orientation forms its own sublattice — two true nests, one P9997 Y-array macro each, trivially editable. An irregular orientation mix from the free packer is a defect on a same-part nest. Engine: nest_rules.fill_y_first(alt_every=True) — rows alternate 0/180 within every column, columns identical. First applied: build 160 (196 pcs, 0-deg lattice 4/col + 180 lattice 3/col x 28 cols; pierce channels 0.75 = lead+standoff shared row-wise between adjacent columns). REFINED same day (Jordan, marked screenshot of 159's print — the correct 'mini-nest' circled, an irregular same-orientation adjacency crossed off): 'approach it like math, you need to find the common denominator, and the smallest pair of parts that together consume the least amount of material. Once you have defined that, you copy that in Y and then X.' The PAIR is the unit: measure its least-material form on true outlines (meshing/phase-shift tried, not assumed), then tile the pair Y-first, then X. Build 161 applies it: long-axis-X orientation (the pierce channel amortizes over the 7.8in column length, not the 1.26in width — 160's 90-deg turn was the wrong economics, superseded), X phase sweep measured <=0.0002in of scallop meshing (irregular lobe spacing — flat-stacked aligned pair IS the minimum, as his screenshot shows), 5 cols x 19 pairs = 190 exactly, gap 0.3000, pierce clear 0.3791, remnant slab 76.9in (was 63.4). COLUMN-FLIP VARIANT + the WHY (Jordan, same day, his MetaCam Edit Array screenshot — 'we talked about this example before': the flip-every-other-column trick, now with its construction order): 'I take the part and maximize Y... the only way I can maximize material is if I share the space of the lead-ins between the parts. So what I do, I flip one part, place it in the correct spot so that the leadin space is shared, and then I copy the whole left column and give it the space that I need for that one flipped part and duplicate that in X. Next, I will take that one flipped part and duplicate that in Y. Lastly, I take that full column and duplicate that in X.' The unit cell is the COLUMN PAIR seeded by ONE flipped part placed for shared lead space — the X-axis form of the mini-nest common denominator. Three standing objectives named: minimize material lost; consistency for the human breaking out the parts; the shortest GCode, editable by one value ('I can change one value and the whole program runs no problem' — the array parameter). Engine: fill_y_first(alt_flip=True) is the slot; the shared-lead tuck offset (the 'correct spot' for that one flipped part, found on true outlines + lead envelope) is NOT computed yet — recorded gap. BUILD ORDERED 2026-08-31 (Jordan: 'Build it'): the alt_flip shared-lead tuck offset — solve the flipped column's position from true outlines + lead envelope instead of measuring it off shop layouts. BUILT 2026-09-01: nest_rules.tuck_alt_flip + fill_y_first(alt_flip=True, tuck=...) — see pin alt-flip-tuck-solver. POSTED 2026-08-31: O33286 (build 163) IS this program — main + two G172/G65P9997 arrays (I=column pitch 8.5526, J=pair pitch 3.1198, K19 Q5; K=Y-rows/Q=X-cols decoded from 33215.NC's own array origins: its third array starts at Y38.433 = .5 + 5x7.5866), subprograms O75286 (0-deg) / O75287 (the IDENTICAL toolpath rotated 180 about the part centre), 212 lines for 190 parts — edit one value, the whole nest follows. nc_audit now transparently EXPANDS P9997 programs and judges the 1140-boundary part-by-part form the machine executes: 0 Rev2 tooling mismatches, geom dev 0.0005, comp all G41; only findings = 950 sanctioned hole centre-pierces (o10/o9 mm < 0.375 standoff), reported never hidden. TUCK-PAIR FORM POSTED 2026-09-01: O33289 (build 164) — the column-flip construction as a delivered program: pair_pitch 16.592 vs 17.105 per two O33286-form columns, 14 columns = 532 pcs on the full 60x120 vs 494; same two-macro shape, sub O75290 = the toolpath rotated 180 and tucked (dx 8.5366, dy 0.4238). See pin alt-flip-tuck-solver.
- •Shared-lead tuck offset SOLVED on true outlines (built 2026-09-01; posted as O33289, build 164) record — The ordered alt_flip engine (Jordan 2026-08-31 'Build it'): nest_rules.tuck_alt_flip(opts, lead, pitch_y, gap, standoff) computes where the one flipped part goes so the lead-in space is shared — slide-to-contact from outside on the TRUE outline + lead/pierce envelope, never bounding boxes. Pair unit: [0-deg col] –shared lead channel– [180 col] –bare gap– next pair; the channel is paid once per PAIR. Constraints per interacting row: ruled outline gap, pierce clear-air BOTH directions, lead-cut clearance (default = gap — a lead is a cut). dy swept and refined; every candidate rides beside the winner with its numbers (nest-objective-function rule); winner re-audited on the fine outline at the ruled values — construction pads 0.010, audit separate (the 3A76 lesson). fill_y_first(alt_flip=True, tuck=...) consumes it: pair columns advance by pair_pitch, odd columns at (dx, dy) rot 180, headroom guard raises with a dy_max re-solve hint; excludes spreads/alt_every/gap_x. Proven: test_tuck.py 22 controls (flat-face closed form recovered at dy=0; tab-nosed part tucks 0.6 under the flat pair; minimality — 0.06 tighter breaks a ruled clearance, measured by an independent shapely check). Smoke on 3683209C2 (Rev2 MS 3/8: lead .375 standoff .375 gap .3): dx 8.5366, dy 0.4238 (capped by column headroom), pair_pitch 16.592 — 14 columns on 60x120 where the O33286 every-channel-0.75 form fits 13: 532 vs 494 parts, X for 5 columns 40.99 vs 42.76 (1.78in saved). Interesting find, proven in controls: even a plain rectangle beats the flat closed form — at interleaved dy the pierce nestles corner-wise into the row gap. DELIVERED 2026-09-01: build 164 / O33289 (deliver_post164_nav209_tuck.py) posts the full-sheet tuck-pair form — 14 cols x 38 = 532 pcs, two P9997 macros (O75289 0-deg 7x38, O75290 180 tucked 7x38 at +8.5366,+0.4238), 212-line program; expanded 3192-boundary audit clean (0 tooling mismatches, geom dev 0.0007, min true-outline gap exactly the ruled 0.3, only findings = 2660 sanctioned hole centre-pierces). Job asked 190 — recorded beside the 532 filled (capacity comparator, Jordan picks which form cuts). Program number 33289 skips reserved 33287/33288 (parked Navistar).
- •The standing nest objective function (Jordan 2026-08-31 — ruling, confirmed 'yes') ruling — ruling. Asked 'what didn't I tell you to make this never go wrong again', Jordan confirmed this articulation: MINIMIZE THE MATERIAL CONSUMED PER PART BY THE FULL REPEATING CYCLE — part, gap, AND lead/pierce envelope together — with every always-apply rule on, tiled so regularly that each orientation is one array, one macro, one editable value, one pattern for the person breaking parts out. Every individual correction (159's irregular mix, 160's wrong orientation economics, the mini-nest pair, the column flip, EXM275FG's dropped leads) is this one function applied to a case. Three consequences, all confirmed: (1) the TOOLPATH IS INSIDE THE UNIT from the first move — lead-in space is material, the common denominator is part+lead+pierce envelope, never bare geometry; (2) a correction on one job is a WINDOW into the rule, never the rule — derive the invariant, don't transplant the example's surface onto the next job; (3) every nest DELIVERY carries the candidate units that were rejected, with their numbers, beside the winner — a wrong pick must show on our sheet, not in Jordan's marked-up screenshot. Self-test ('approach it like math'): if we can't DERIVE Jordan's layout from the stated objectives, we don't understand yet — measure until we can, don't wait for markup.
- •One-click FINISH EVEN COLUMNS — round qty up to fill the last column (Jordan 2026-08-31) intentmixed — intent + engine record: 'we should include a one-click option to finish even columns in Y, meaning we asked for 190 pcs, but we can fit a few more to finish that last column (and also end up with cleaner GCode - one set of P9997 macros) so the total might end up being 194 with clean, even columns.' The nesting tab offers a one-click 'finish columns': qty rounds UP to n_cols x per_col so no column runs partial and the Y-array macro pattern stays uniform. Operator's click, never a default (it cuts more parts than were asked for); the asked qty is recorded beside the filled qty. Engine slot IS built: nest_rules.fill_y_first(finish_columns=True); first applied build 160 (asked 190, filled 196 = 28x7).
- •Interface intent — nest spacing prompt: table default, operator shrink OR spread-out, both layouts shown (Jordan 2026-08-27) intent — intent: at the nesting step the spacing (gap + derived margin) defaults from the Rev2 min_gap; the operator can shrink it per job — 'this should be easy to do in the interface.' And the interface should SHOW the trade: Jordan's worked example (0.23 vs 0.25 on LS1156258 = one whole column, over 5in of X) is the template — 'You should give the operator both options and they can pick.' So the nesting tab presents the ruled layout beside tightened alternative(s) with the material saved, and the operator picks. Same day he added the opposite command: SPREAD IT OUT — with large parts, small gaps and plenty of scrap at the top of the columns, one command evens out all the gaps, which spreads the heat load and reduces thermal-expansion error during cutting. Engine: nest_rules.fill_y_first(spread_y=True). And X: the auto spread never touches the clean remnant slab, but the OPERATOR gets manual X-spacing increments (his MetaCam Layout Array demo: X spacing 0.23 -> 1.03 walks the columns across the sheet) — engine slot fill_y_first(gap_x=...). The resolved value is a STARTING POINT, not a floor — the operator may override below it ('just your starting point'). And it's ONE MODAL: shrink to max Y + max remnant, spread out, or FLIP EVERY OTHER COLUMN — 'one trick that us humans do that no nesting software does, and we want you to do it': 180 the odd columns so pierce points align in Y, X spacing unchanged, squeezing in a whole extra column on a tight sheet. Engine: fill_y_first(alt_flip=True). Sibling intents: free-rotation toggle, nest-tooling feedback loop, per-contour override. Phase rule: record here, do NOT build the UI. ANSWERED 2026-08-27: the tight-nest demo's 180-pair element was teaching only (production 28585 runs one orientation); shortening lead-ins / moving pierce points in tight nests is MANUAL operator work, never an automatic default; and the modal's array face belongs to TRUE NESTING (same part duplicated, toolpath before nest) — see pin def-true-vs-part-by-part-nesting. ONE-CLICK CONFIRMED + REFINED (Jordan 2026-08-28, live-nest screenshot, 3 cols x 18 rows, X 0.2/Y 0.32): he manually evened the Y gap — top, bottom, AND between parts — for thermal-expansion control, and asked for one-click 'spread Y', 'spread X', and 'back to table values'. The spread evening now includes the edge gaps (supersedes edge-margins-stay-put); spread X spends the clean remnant slab so it is only ever the operator's click; reset reseeds from the Rev2 min_gap. Engine updated same commit: fill_y_first(spread_y=True / spread_x=True), defaults = the reset.
- •THE PHILOSOPHY — aid the operator, never decide for the operator (Jordan 2026-08-27) ruling — ruling: 'We are here to aid the operator, not to decide things for the operator. That is the whole philosophy of our software, we need to wrap ourselves around the workflow of the customer instead of forcing the customer to adhere to our limitations.' Said while overruling our lower-right pierce bias with the flip-every-other-column trick. Table values seed, never bind; biases guide, never block; every automatic decision stays operator-overridable and the override is recorded, not resisted.
- •A beat only counts with every always-apply rule in force (Jordan 2026-08-31 — ruling) ruling — ruling, on EXM275FG-0625: 'once I challenged you, you tried to beat me without applying the rules. That is pleasing behavior, and not acceptable in our environment. So, in this case you dropped the lead-ins like I pointed out, and that is one of the rules that always have to be applied. Look, I expect you to beat me, but not by cutting corners.' Build 157 delivered a 99-vs-94 nest count with UNTOOLED geometry — no leads in the envelope — against his measured layout that carries lead+standoff room; build 158 (tooled, still 99) is the honest form. The rule: a comparison against the shop only counts when every always-apply rule (lead-ins at ruled length, pierce clear-air, gaps, cut-order safety) is applied to OUR side; a count achieved by dropping one is not a result, it is pleasing behavior. When a rule must be relaxed to match a shop layout, say so explicitly — never silently omit it.
- •Print DIMENSIONS govern over drawn geometry (Jordan 2026-08-31 — ruling) ruling — ruling: 'The goal for us will be to have a program that can read pdf prints and generate a dxf from the called out dimensions. If the customer can only provide a pdf print with dimensions then it is ethical of us to assume the stated dimensions are correct, even if the pdf is drawn slightly off.' Where a callout and the drawn geometry disagree, the callout wins; undimensioned features follow the design intent the drawing shows (concentric, tangent, flush), re-solved around the dimensioned ones. The trace becomes the VERIFICATION layer — deltas measured and reported against the print's tolerance. First applied: nav209_part.py build 160 (supersedes 159's internally-consistent-drawn call on holes 3/4: callout ys 123/151, lobes+waists re-solved, trace verifies at 0.333mm max inside +-0.8).
- •True nesting vs part-by-part nesting — two kinds, two pipeline orders (Jordan 2026-08-27) ruling — ruling: same part duplicated over and over = TRUE NESTING — the toolpath is established FIRST on the single part, then the nest duplicates one short piece of G-code per placement. Several unique shapes on one sheet (Jordan's term: Torongo files) = PART-BY-PART NESTING — nest first, toolpath after. The lead/cut-order and nest stages swap depending on which kind the job is.
- •Part-list nesting — Qty / Rotate / Bridge / Filler columns (Jordan 2026-08-31) rulingmixed — ruling+intent: part-by-part nesting is driven by a PART LIST — per part: Qty (0 = listed but excluded), Rotate step (360 = locked, 180 = 0/180, 90 = quarter steps; the grain-direction click writes this), Bridge = per-part gap seeded from the Rev2 min_gap — OUR semantics: the TOTAL gap around that part (MetaCam's half-sum .15+.15=.3 rejected as confusing), operator-overridable per part, larger value governs each pair (confirmed) with the small-bridge hunt as the recovery lever; Filler flag = this part may be duplicated beyond ordered qty to fill the sheet — default UNCLICKED: never fill, maximize Y, keep the X remnant. bridge also governs the part's SHEET-EDGE clearance (per-part margin); filler extras ship asked-qty beside filled-qty and obey bridge/rotate like ordered parts; Priority column: ignored, ruled out. Benchmark: MetaCam put the LH-2.5 340-part 3/8 job on 5 sheets 60x120 — we need not beat 5, but 7 is a fail (job-specific target, education only — no deliverable; a real test job follows). HOME ruling 2026-08-31 (Jordan): the packer engine is still to be built ('we will spend the next few days on that'), but the part-list columns already get their HOME in the nesting tab — the per-part grid (qty/rotate/bridge/filler) is part of the nest stage's data model now, so the packer plugs into existing fields when it lands. ENGINE BUILT 2026-09-01: part_list_nest.pack() -- see pin part-list-packer; every column semantic above is enforced in code and proven in test_part_list.py (24 controls).
- •Part-list packer ENGINE — part_list_nest.pack() (built 2026-09-01) record — record: the part-by-part packer driven by the ruled part-list columns (pin part-list-nest-columns) is BUILT: part_list_nest.pack(items, sheet_w, sheet_h, table_gap, standoff) -- pure geometry engine, no UI (phase rule). Per part: qty (0 = listed-but-excluded, quantities split across sheets), rotate step (360/0 = locked, 180, 90, any divisor of 360; non-divisor raises), bridge = the FULL clearance around the part (None = table_gap seed recorded source 'table', explicit = 'operator', either direction), filler flag (default off: never fill, max Y, X remnant kept; on: extras fill OPENED sheets only, never a new sheet, obey bridge/rotate exactly, asked-qty reported beside filled-qty). Pairwise gap = max of the two bridges, enforced by a per-bin Pareto frontier (right-x, owner bridge) in the sliding profile so each pair is charged its own requirement; the bridge also sets that part's sheet-edge clearance (per-part margin). Fill-Y-first via the nest_y_first transposed frame (a row = a real full-height column; remnant = one clean X slab, used_x reported per sheet); placement score = projected real-X use, so a rotation variant only turns the part when it buys X back. Leads: caller passes pre-planned lead/pierce samples (split_parts lineage); lead-to-neighbour clearance = max(standoff, pair gap). The engine AUDITS its own result from the placed points at the ruled values (construction slides on padded bins -- separate arithmetic, the 3A76 lesson) and RAISES on any miss; report carries adjacencies (required / governed_by / measured), bridges with sources, excluded list, and 'hunt' = every column advance that paid a neighbouring column's larger max-bridge (Jordan's regrouping lever; equal-size parts already group by bridge in the sort). Terminology guard: this 'bridge' is MetaCam's part-GAP column, never bridges.py's island bridge. Proven: test_part_list.py, 24 controls on synthetic fixtures -- no delivery yet; Jordan's real test job is the first fixture when it lands (LH-2.5 was education only).
- •MetaCam Basic tab — the CAD editor's reference feature set (Aristide + Jordan, 2026-08-31) record — reference: Aristide read the MetaCam CAD sidebar's Basic tab out block by block (Jordan supplied the screenshot, incoming/1788211410_645725_file.jpg). Three tabs exist — Basic / Advanced / Dimension; this covers Basic only. Its edit/QC block (Fit Arc, Join, Remove Small Segments, Mark Open Polylines, Highlight Duplicate Segments, Mark Small Segments, Mark Non-Tangent Corners, Mark Large Radius) is our healing pipeline as MANUAL buttons — same defect taxonomy, opposite philosophy (operator finds+fixes each; we detect+heal+report). Corner-treatment block (Chamfer, Fillet, Inside Fillet, Corner Step, Edge Recess, Edge U-Cut, Edge V-Cut) not yet in our catalog. RULING added same day: automated or not, 'all buttons like these need a home' — automation sets the default, the manual button stays as the operator's override. General (Windows-standard) top toolbar also recorded in detail.
- •MetaCam Advanced tab — notches, booleans, splines, sheet-metal 3D (Aristide + Jordan, 2026-08-31) record — reference: Aristide dictated the Advanced tab (Jordan's screenshot incoming/1788211783_645725_Advanced_Tab.JPG confirms the block structure). Notch suite = manual identical-feature propagation; booleans (union/intersection/subtraction) = our filled-art union lessons as buttons; spline tools rebuild what MetaCam's own DXF import drops; the sheet-metal 3D block (flange/plane joining, follow to 3D model, plane extract, Edit bend information) is their face of our step_io/unfold territory — Edit bend information is where the open K-factor question lives; Mark text vs TrueType text = the etch-vs-cut split on text. Dimension tab still to come.
- •MetaCam Dimension tab — the dimensioning toolbar Jordan uses daily (Aristide + Jordan, 2026-08-31) record — reference: Aristide dictated the Dimension tab (Jordan's screenshot incoming/1788212237_645725_Dimension_Tab.JPG confirms the blocks) — 'Jordan says he uses these a lot,' so dimensioning is a daily operator activity, not a drafting afterthought. Linear block = a 4x3 matrix (horizontal/vertical/aligned/perpendicular x baseline/continue/ordinate) + smart dimension; radial/angular block (radius center, radius diameter, center diameter, angular, 3-point angular, callout, segment, multi dimension); properties/auto-dimension/text/edit-text/view-legends; view block. Completes the three-tab set (Basic/Advanced/Dimension). Ties to make_prints dimensioned pages and pdf-dims-govern — the shop lives in callouts.
- •UI BUILD PHASE OPENED (Aristide, 2026-09-01 — ruling) ruling — Aristide, 2026-09-01: 'go ahead. let's go. you have all the information. start building the UI.' The owner's call opens the interface as a BUILDABLE thing — this supersedes the 2026-08-02 phase rule's 'do not build the interface' for the UI itself; every definition-phase ruling, gate and table stands unchanged, and the UI is built strictly on the recorded design corpus (paper-design signed off 2026-08-17; sitting answers 2026-08-19 incl. the A+C pick; ui-one-source-three-lenses; aid-the-operator-philosophy). Build order: turn-sized bricks, engine-first, honest placeholders for unwired stages.
- •kerfmaster app — brick 1: shared-engine server + canvas shell + Jobs home (2026-09-01) record — BUILT (app/): the one-source-three-lenses architecture in code. app/server.py — stdlib-only HTTP door over the SAME engine modules the boards run (dxf_io.parse → to_inches → heal.heal → find_jogs → qc.analyze, fidelity vs true source with the board's own measure); the job is a JSON document (app/jobs/<id>/job.json + geometry.json) — the one source of truth every shell renders; Web/Mac/PC = the identical renderer (app/web/, plain HTTP+JSON+canvas, nothing platform-only), native shells will wrap it, never reimplement. Shell per the rulings: canvas owns the screen; eight top tabs (Intake…Deliver) wearing status colours PLUS glyphs (✓/!/✕ — the colour-blind-safe half of agenda item 4, done cheap-now); Jobs home front door with per-job mini tab-strips + drop/browse intake; left toolbar (contents to grow per tab), right checks drawer (A+C dock ground state; tear-off/workspaces = later brick); original DXF as toggleable gray base layer under healed overlay (house style); zoom %% readout + presets AND self-proving scale bar in user units (zoom ruling); light/dark one-click; per-user units pref, inputs accept unit suffixes (3mm/0.125in); one-letter tool keys (V/F/O/H/J). Blocked intake = red tab + engine's own message, warn-never-block elsewhere. Unwired tabs say so honestly in the drawer. Persistent: systemd kerfmaster-app (unit in repo, setup_app.sh idempotent) + mca-host sub kerfmaster-app → https://kerfmaster-app.aristide.mycloudally.com — verified live over the public URL (ex2_dirty job: mm detect, heal PASS 1.08 thou, 19 QC flags, auto-lands on the flagged tab). NOT YET (brick list): nest/cutplan/post/deliver screens, sketcher, tear-off panels, sales surface, first-run tour. SHORTCUTS FIT THE SCENE 2026-09-17 (Jordan: 'Shortcut keys should also be global. Right now I'm in the Nest tab, and F isn't working'): the F key and the palette Fit button were hardwired to fit('fitpart') -- dead on every sheet view, and silently a no-op on part-list jobs, which carry no part extents at all. fitScene() now makes the same sheet-vs-part pick the tab click and deep link make; fit() itself falls through to the sheet extents when the job has no geometry of its own; and typing in a focused SELECT/TEXTAREA no longer leaks into the shortcut keys (only INPUT was guarded). Verified via Playwright on the CAMP part-list Nest tab.
- •Part review screen (UI brick 2) — the per-contour override ruling on glass record — BUILT (app/, brick 2 of the 2026-09-01 build-them-all go): the Part review tab is the 2026-08-24 right-click ruling running live. Operator assigns material × machine from the Rev2 tables (only rows with a recorded shop post code are offered — Mild Steel ↔ MSO7, the deliver-script pairing; no invented mappings) and the server plans the toolpath through rules.check_drag — the SAME click-time entry point the drag validator uses (new want_stats flag returns the plan it judged: one plan, one judgment, no drift). Canvas draws the AS-CUT paths over the gray base layer: sequence numbers, pierce rings, dashed lead-ins, finding contours tinted red/yellow. Click or right-click a contour → checks drawer shows EVERY ruled field as ran-value + source (class, condition #, comp register D, comp side, direction, sequence, start/pierce, clear air, lead, tab — tooling_doc's record verbatim) plus override slots: start/pierce = pick-on-canvas (the drag-handle model's click form), lead length = unit-suffix input; both re-plan through the engine's overrides= seam and the ran value flips to source:operator — seed never bind, warn never block, recorded never silent. Non-consumable slots say honestly their engine inputs aren't built. Findings list red-first with the shared rule messages; stage dot done/flagged/blocked (engine refusal = blocked + its own words, e.g. beam_comp TBD rows). tooling.json + review.json persist in the job dir; deep link #job=<id>&tab=review. Verified live (ex2_dirty: 7 contours tooled, 2 red / 4 yellow honest findings, override round trip lead 0.15→0.08 source:operator, test_drag 10 + test_tooling_doc 15 controls pass). NOT YET: drag as one continuous handle (click-pick form only), remaining brick-list screens.
- •Nest screen (UI brick 3) — the layout-array modal rulings on glass record — BUILT (app/, brick 3 of the 2026-09-01 build-them-all go): the Nest tab is the layout-array modal rulings running live, on the ruled engines only. Server POST/GET /api/jobs/<id>/nest: TRUE-nests the REVIEWED part (material x machine come from the Part review stage -- toolpath before nest for a duplicated part, ruling def-true-vs-part-by-part-nesting) through nest_rules.resolve_spacing (gap AND edge margin seed from the Rev2 min_gap; operator overrides run in either direction -- starting point, never a floor -- and every value records ran + source: table:min_gap / derived:gap / operator, never silent) and nest_rules.fill_y_first (fill Y first, remnant one clean full-height slab; spread_y / spread_x / gap_x / alt_flip / alt_every / finish_columns are the operator's ruled one-clicks). Multi-sheet by per-sheet capacity, remainder on the last; sheet = the machine bed via tables.bed() (120x60 X-major); engine refusal = blocked stage with the engine's own words; nest.json persists per job. Drawer = the layout-array modal: qty, gap/margin/X-spacing unit-suffix inputs whose placeholders show the ruled current value (min_gap now rides on /api/materials options), orientation select (all one way / flip every other column / every other part 180), finish-even-columns checkbox with asked -> filled recorded beside each other, one-click spread Y / spread X / back-to-table-values (spread X labelled as spending the remnant slab -- operator's click only). Canvas draws the sheet + dashed margin, every placement stroked from the REAL healed geometry (one cached true-arc Path2D placed by canvas transform, 180s rotated about the part centre), remnant slab tinted, per-sheet caption. Defect caught + fixed in the build: the view extents pad arcs by their full circle -- nesting on them inflated the 209 part 1.26 -> 3.456 in wide; the job document now carries part_bbox (true sampled bbox) and the nest grid + renderer run on it. Verified live over the public URL on the real 209 part (MS 3/8, Rev2 min_gap 0.3): 190 asked -> 196 filled = 28 cols x 7/col, the recorded build-160 finish-even-columns numbers reproduced exactly; alt_flip renders the odd columns 180; gap 0.23 operator override records source:operator; remnant slab 76.3 in. HONEST LIMITS stated on-screen: grid nest on the part bounding box; leads/pierce envelope not in the nest unit yet -- the nest<->tooling loop, tuck pairs, part-list packing and free rotation have engines but not this screen (later bricks).
- •Cut plan screen (UI brick 4) — the toolpath view judged by the shared rules record — BUILT (app/, brick 4 of the 2026-09-01 build-them-all go): the Cut plan tab is the toolpath view (Jordan 2026-08-28, pin toolpath-view-intent) as a live screen -- the path the laser will follow, judged at click time by the SAME shared rule functions the NC audit runs. Server POST/GET /api/jobs/<id>/cutplan: the sequence IS review.json's plan array (the engine's own safety order -- outer profile last, safety before distance; requires Part review first, refusal in the engine's own words), judged by rules.check_plan with the audit's own parameter sourcing (tables.drop_through / tables.standoff(material)) -- one plan, one judgment, no drift with the final posted-NC gate. Document per step: seq, bnd id, outer/interior, comp word, cut length, straight-line rapid in (from previous contour end to this pierce) with its length; totals cut travel + rapid travel; findings red-first; cutplan.json persists; stage done/flagged/blocked. A re-plan in Part review DELETES the persisted cut plan and resets the stage to pending -- a stale judgment never keeps serving (one source of truth). Renderer: as-cut paths tinted by finding severity, TWO direction arrowheads per contour along the cut, pierce dots, dashed rapids with an arrowhead at the landing pierce (drawn under the cuts), sequence number at each contour; drawer = ordered cut-order list (circled seq badges tinted by severity, click to select, selection syncs with canvas + right-click), findings rows, totals. Honest limits on-screen: Manual Sort (click-in-order) is recorded intent -- the operator sequence is not a consumable engine input yet, so the screen shows the engine's order and where it would warn; rapids are straight lines (rapid routing around held slugs = the recorded unbuilt Traverse Routing lever), crossings counted never hidden. Verified live over the public URL: 209 part 6 contours outer-last 0 red / 6 yellow; ex2_dirty 7 contours 2 red (wrong-side comp, cut-after-severed-outer) / 4 yellow; suites test_drag 10 + test_tooling_doc 15 + test_rules 11 pass. Deep link #job=<id>&tab=cutplan. [2026-09-01, brick 6: Manual Sort is BUILT and consumable -- the click-in-order UI lives on this screen; see ui-manual-sort-brick6.]
- •Post screen (UI brick 5) — the posted program, audited, with the warning callouts in the file record — BUILT (app/, brick 5 of the 2026-09-01 build-them-all go): the Post tab turns the reviewed toolpath into a real Fanuc macro-B program with the audit and warning callouts riding IN the file. POST/GET /api/jobs/<id>/post + GET post.nc|post_tooling.json|post_slugs.csv. The server posts the SAME engine inputs Part review planned with -- material x machine tooling row (tables.rev2_tooling) + the operator's recorded overrides read back through tooling_doc.overrides_from(tooling.json) -- through emit.write_gcode_macrob in the master dialect, so the file IS the reviewed plan, posted; then the one shared sequence every posting surface runs: nc_warn.audit_and_inject -- audit vs source (Jordan 2026-08-12), (CONTAINS N WARNINGS) + per-boundary (WARNING k: SEV RULE) callouts written into the G-code (Jordan 2026-08-28), re-audit must judge identical. Warn never block: findings/anomalies flag the stage; only an engine refusal (MaterialIncomplete etc.) blocks it, in its own words. Program number per nest-name-prog-number: auto-assign next = last used + 1 (app/progseq.json seeded 33289 = this box's last posted; 9000-series macro space and parked 33287/33288 skipped), operator-overridable (recorded source:operator; since fix block 3 the override is USED ONCE by default -- the sequence does not move -- or restarts the sequence when the operator says so, reserved/used numbers a yellow warning, see app-onum-modes-block3), and a job KEEPS its number across re-posts. A re-plan in Part review deletes the posted files and resets the stage keeping the assigned number -- a stale program never keeps serving. Every post writes the tooling doc beside the NC (tooling-doc-model) and the numbered slug report when slugs exist. Drawer: program/material/machine/dialect/moves/extent as ran+source, download links, findings red-first (click = select contour), the audit's anomalies verbatim, first-24-line program preview; canvas = the cut-plan (or review) scene the program was posted from. Verified live over the public URL: 209 part O33291 0 red/6 yellow, (CONTAINS 6 WARNINGS) + 10 spot markers in-file, 134 lines; ex2_dirty O33290 2 red/4 yellow incl. wrong-side comp DO NOT RUN; operator onum round-trips to source:operator. Honest limits on-screen: posts the single reviewed part (the nested sheet program -- post_nest, P9997 macros -- is a later brick); operator sequence still not a consumable engine input; program-number scope (shop-wide vs per machine) open with Jordan. [2026-09-01, brick 6: operator sequence IS a consumable engine input now -- it rides into the post through overrides_from like every override; see ui-manual-sort-brick6.]
- •Manual Sort (UI brick 6) — the operator sequence as a CONSUMABLE engine input, click-in-order record — BUILT (brick 6 of the 2026-09-01 build-them-all go): Jordan's click-in-order model (2026-08-28, metacam-add-toolpath-walkthrough -- 'exactly what we envisioned for the user interface'; never MetaCam's broken modifier keys) is now a consumable override end to end. ENGINE: 'sequence' joined tooling_doc.CONSUMABLE (same commit as the input, per the model rule); emit.write_gcode_macrob overrides= takes {'near','sequence':k} per feature, nearest-centroid binding like start/lead_length -- sequenced features cut FIRST in click order, unclicked keep the engine's safety order after them; the operator's order gets its rapids over held slugs RECOUNTED with the same _seg_hits_poly test (counted and reported, never silent); an outer clicked first takes its approach from home; sequence recorded per contour as ran+source (operator vs rule:safe-order) and the click index beside it in overrides.sequence; overrides_from round-trips it, unconsumed() no longer lists it; overrides=None stays byte-identical. Moves the ORDER, never a gate: a risky order fires the same shared rules (check_plan/check_drag == nc_audit -- outer_not_last red proven both surfaces, test_drag control 7) and rides into the posted NC via the existing nc_warn callouts. APP: run_review consumes sequence through the same override channel; run_cutplan judges the operator's order (sequence_source names it + operator_sequenced count); run_post consumes it via overrides_from like every override -- honest-limit notes updated. UI (Cut plan tab): Manual Sort section -- Click in order... mode (canvas clicks add/unpick, accent pick badges), Apply re-plans + re-judges, Back to engine order clears; the P9996/P9997 cost notice (Jordan 2026-08-28 ruling: a custom sequence drops the nest macros, program posts part-by-part) shows at the first departing click -- informational, never a block; review drawer's sequence slot is live (clear per contour). Defect fixed on the way: runReview read material/machine from the review drawer's selects only -- a re-plan from another tab silently no-opped; now falls back to the assigned review row. Verified live over the public URL (app/verify_seq.js, Playwright): 209 part -- all 6 contours clicked outer-first ran as clicked (source:operator on all 6), outer_not_last RED fired and rode into O33291's G-code, number kept across re-posts, Back to engine order restored 0-red clean. Suites: test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass. Honest limits: nest-LEVEL part sequence (nest-part-sequence, serpentine default + numbered boxes) is a later brick; rapid routing still unbuilt (crossings counted, shown).
- •Deliver screen (UI brick 7) — the delivery set: versioned, census-checked, boarded record — BUILT (brick 7 of the 2026-09-01 build-them-all go): the Deliver screen -- the posted program becomes the DELIVERY SET, versioned and boarded. Server: POST/GET /api/jobs/<id>/deliver (+ GET /deliver/v<k>/<file>) requires the Post stage (refusal in honest words); writes an IMMUTABLE VERSION dir (jobs/<id>/deliver/vNNN) holding: the audited O<num>.nc (warnings riding in the file), the MetaCam-safe DXF written by emit.write_dxf_metacam (the ruled writer for anything a human downloads) and CENSUS-CHECKED on the written file (only POLYLINE/VERTEX/SEQEND/CIRCLE; a census FAIL refuses the delivery), the numbered slug report, the tooling document, the FLOOR PACKET (Aristide 2026-08-19 ruling, PINLIST 7c: floor operators are file recipients, not app users -- program number big, material, machine, extent, slug count by number, part preview SVG, time estimate honestly PENDING, usage honestly n/a for a single part) rendered from packet.json (data in JSON, generator separate from presentation), and a full-set zip (design 3.8's folder/USB handoff). Job-versioning ruling (Jordan 2026-08-28) applied: append-only chain, delivery is the trigger, versions immutable forever -- a re-delivery appends v(n+1) and marks older versions superseded (metadata only, files never touched); a re-plan in Part review deletes the posted program but NOT deliveries -- it marks the deliver stage 'replanned' so the screen says v<n> predates the re-plan. Every delivery is also a NUMBERED BUILD on the comparison board (boards.py, kind 'app' -- one build sequence across DXF/vector/app so it can be said out loud), board page rendered from the same version dataset: downloads, warnings banner, findings + audit anomalies verbatim, census result, part SVG. Warn never block: ex2's 2-red program delivers flagged with the reds on every page and in the file. UI (Deliver tab, placeholder retired): Deliver button, DELIVERED v<n> banner, census/slug/program rows, board + packet + per-file download links, version chain list with superseded badges, honest 'by whom awaits operator accounts'. Verified live over the public URL (app/verify_deliver.js, Playwright, 12 checks): nav209 chain v1..v3 (builds 165-167) superseded correctly, number O33291 kept on every version, board build serves publicly, packet + NC (CONTAINS N WARNINGS) + zip download, DXF census independently re-checked on the served bytes; ex2_dirty v1 (build 168) flagged 2 red. Suites: test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass. Honest limits on-screen: single reviewed part (nested-sheet delivery = later brick); the packet is SHOP-facing -- the customer sheet of the ruled customer/shop split is a later brick; time estimate PENDING (no estimator); 'delivered by whom' waits on operator accounts. Verify-script lesson re-learned: a hash-only goto does not reload the SPA -- the first run's ex2 section delivered nav209 v3 instead (build 167, immutable, stands as the honest record); fresh page per job.
- •Nested-sheet post (UI brick 8) — the P9997 macro program from the app, part-by-part when Manual Sort rules the macros out record — BUILT (brick 8 of the 2026-09-01 build-them-all go): the nested-sheet program posts from the app. Server: POST/GET /api/jobs/<id>/postnest (+ GET postnest.nc / postnest_tooling.json / postnest_slugs.csv) arrays the REVIEWED toolpath (same engine inputs run_post posts: Rev2 tooling row + overrides via tooling_doc.overrides_from) over the Nest tab's persisted layout. Exactly TWO shapes per Jordan's 2026-08-28 ruling (Mitsubishi controls won't mix nest macros and part-by-part sequences): MACRO mode (default) = the 33215.NC true-nest form via emit.write_gcode_macrob(nest=...) -- main O<n> with G172 origins + G65P9997 calls, one sub per orientation (two orientations = two P9997 macros, 2026-08-31 ruling), subs main+42000 (corpus convention, ranges question open); the app decomposes the persisted placements into regular sublattice array blocks with span-derived pitches (successive rounded deltas fragmented a perfect lattice on the first run -- fixed; a partial last column becomes its own block; a genuinely irregular layout refuses with its own words). PART-BY-PART mode: any Manual Sort sequence override drops the macros -- the program posts fully expanded, every placement's features in the operator's per-part order (replicated per copy with transformed near/start points), parts in placement order. Audit: nc_warn.audit_and_inject on the file, source = the placed geometry; the audit EXPANDS the macro program to the part-by-part form the machine executes (nav209: 6 program bnds -> 1176 executed) and the callouts ride in-file with spot markers mapped back into the subs. nc_warn._san now truncates a comment line past 160 chars honestly ([TRUNCATED - FULL TEXT IN THE AUDIT DOCUMENT]) -- a 196-part expanded finding named 940 boundaries in one 4KB comment line, a control-format hazard in the arc-center-ceiling spirit. Stale-program rule extended: a re-plan in Part review OR a re-nest deletes the posted nest program, keeping its assigned number (job keeps its number across re-posts, proven both modes). UI: 'Nested sheet program' section on the Post tab (sibling of the single-part post -- renders even when no single-part program exists), form + why + arrays + parts + warnings + downloads; Manual Sort cost notice repeated at the post itself. Verified live over the public URL (app/verify_postnest.js, Playwright, 12 checks, fresh page per job): nav209 macro O33292 two 14x7 arrays, scratch job part-by-part O33293 with outer_not_last RED riding in the file. Suites test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass. Honest limits on-screen: posts sheet 1 (multi-sheet = one program per sheet, later brick); nest-level part sequence BUILT as brick 15 (ui-nestseq-brick15) -- serpentine default consumable, part-by-part order clicked-first-then-serpentine; nested-sheet DELIVERY later brick; slug tabs ride the same legacy defaults as the single-part post (needless-tab yellows are the audit's honest output).
- •Nested-sheet delivery (UI brick 9) — the posted nest program as its own versioned delivery set, with real sheet usage and the placement→part map record — BUILT (brick 9 of the 2026-09-01 build-them-all go): the posted NESTED-SHEET program ships as its own DELIVERY SET. Server: POST/GET /api/jobs/<id>/delivernest (+ GET /delivernest/v<k>/<file>) requires the posted nest program (refusal in honest words) and writes an IMMUTABLE version dir per delivery on its OWN append-only chain -- a SIBLING of the single-part chain, same job-versioning ruling (append-only, re-delivery appends v(n+1) and supersedes older versions -- metadata only, files never touched). The set: the audited O<num>.nc (callouts riding in the file), the MetaCam-safe DXF of the WHOLE PLACED SHEET (every placement's healed geometry transformed to its nest position, emit.write_dxf_metacam, CENSUS-CHECKED on the written file -- census FAIL refuses the delivery; nav209: 196 POLYLINE outlines + 980 CIRCLEs, nothing else), the numbered slug report + tooling doc, the PLACEMENT->PART-NUMBER MAP as a CSV (Jordan 2026-08-31 nest-summary ruling: 'part 001 is BNM78655' -- single-part true nest, every placement numbered anyway), the NEST FLOOR PACKET with REAL SHEET USAGE (net part area x parts / sheet area, shoelace on densely sampled TRUE outlines -- the benchmark's own measure, never bounding boxes; nav209: 196 x 8.2572 in2 on 7200 = 22.5%), program form (macro subs+arrays or part-by-part), qty asked beside filled, time estimate honestly PENDING, and a full-set zip. Every delivery = a NUMBERED BUILD on the comparison board (kind app, one sequence). Stale rule: a re-plan OR a re-nest marks the nest delivery 'replanned' (screen says v<n> predates it) while its files stand immutable; the postnest program is dropped, number kept. Warn never block: the part-by-part fixture delivers flagged 1 RED, the RED riding in the delivered NC. UI: 'Nested sheet delivery' section on the Deliver tab, sibling of the single-part set (renders without one); banner / usage / part map / census / downloads / version chain. DEFECT CAUGHT IN THE BUILD: a client disconnect during the HTTP reply (broken pipe on a piped curl) was recorded as a BLOCKED stage though the delivery had succeeded -- ALL POST routes now write the response OUTSIDE the blocked-marking try, so a transport failure can never falsify a stage record; nav209's false record repaired to the truth. Verified live over the public URL (app/verify_delivernest.js, Playwright, 13 checks, fresh page per job): nav209 macro chain v1-v2 supersedes with O33292 kept, packet/board/map/zip serve publicly, served sheet DXF re-censused INDEPENDENTLY on the bytes, re-nest->replanned->re-post->number-kept loop proven, brick8-seq fixture O33294 part-by-part 1 RED in-file. Suites test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass. Honest limits on-screen: sheet 1's program only (multi-sheet = one program per sheet, later brick); packet SHOP-facing (customer sheet of the ruled split later); 'by whom' awaits operator accounts. ROTATION FIX 2026-09-06 (Jordan's catch on 44558B_10 v2 -- 'nest floor packet doesn't appear to represent the nests/layout accurately'): run_delivernest rebuilt the placed sheet with a 0/180-only shortcut predating the any-angle hand editor, so a hand layout's 90-degree placements shipped drawn as 180s in the sheet DXF and BOTH packet previews (the posted program was correct -- the newer per-orientation path). Fix = ONE shared placement function, server._place_prims (rotate about the part centre, 180 keeping the exact closed form), consumed by run_postnest's place() AND the delivery rebuild -- post and shipped drawing can never rotate differently again. v2 stands as delivered per the versioning rule; v3 is the corrected set. Proven: test_nestdeliver_rot.py 14 NEW controls (90 corners + off-centre hole at the rotated spot in the written DXF bytes, 180 byte-equal, automatic-nest regression) + 7 neighbour suites green.
- •The sketcher (UI brick 10) — dimensioned primitives typed as numbers, compiled into a real source.dxf, born clean into the normal pipeline record — BUILT (brick 10 of the 2026-09-01 build-them-all go) -- the ruled v1 scope of sitting answer 8 (Aristide 2026-08-19: 'a real sketcher' -- lines, arcs, circles, all assignable numerical values; the dimensioned-primitive sketcher, NOT a constraint-solver CAD; born-clean geometry rides the normal pipeline from QC on). ENGINE: sketch.py compile(doc) turns the typed document (inches; angles in degrees; circle by DIAMETER, the shop's word) into the pipeline's own prim form, stitching typed segments into contours by endpoint agreement -- lines are the flexible member (a line endpoint within 5e-4 snaps TO an arc's computed endpoint; arcs are rigid, moving their ends would corrupt the radius), arc-to-arc joins must already agree to the 1e-4 contour gate with a near-miss reported in honest words, circles compile as two semicircle bulge arcs (dxf_io's own CIRCLE diet) and come back out of the MetaCam-safe writer as true CIRCLEs. A bad sketch refuses with its own words (SketchInvalid: empty sketch / zero-length line / non-positive diameter or radius / zero-sweep arc = 'use the circle primitive' / non-number); an OPEN chain is NOT a refusal -- QC's open-contour flag is the ruled surface (warn never block). SERVER: POST /api/jobs?sketch=1&name= starts a job from a BLANK SKETCH (no file); GET/POST /api/jobs/<id>/sketch saves the document on every edit (typed work never lost -- saved BEFORE compile, proven on the refusal path) and with apply=true compiles -> writes a REAL source.dxf via emit.write_dxf_metacam (the ruled writer) -> runs the SAME run_pipeline every customer file rides; nothing downstream knows the sketch exists. Aristide's own ruled example (the 2" OD / 0.5" ID customer) round-trips at heal fidelity EXACTLY 0 with QC clean -- born clean, measured, not asserted. RE-COMPILE = the geometry changed under every downstream stage: run_pipeline (now merge-aware) drops review/nest/cutplan/post/postnest documents (a stale judgment never keeps serving), KEEPS the job's assigned O-numbers on the pending stage records, and marks delivered versions replanned -- immutable files untouched, per the job-versioning ruling. An edit WITHOUT apply marks the sketch stage stale so the screen says the compiled geometry predates the typed values. UI: 'New sketch' on the Jobs home; a Sketch tab AHEAD of the standard eight on sketch-born jobs only; primitive rows as unit-suffix inputs (mm accepted anywhere), add/delete, live canvas preview of the typed geometry over the gray base layer, Apply, refusals and compile notes in the engine's words, the scope note on-screen ('dimensioned primitives, not a constraint solver; drawing by mouse is a later brick'). Verified live over the public URL (app/verify_sketch.js, Playwright, 13 checks, fresh page per phase): washer typed -> applied -> reviewed -> POSTED as O33296 (a sketch becomes a numbered program), ID edited 0.5->0.75 -> stale marker -> re-apply drops the program and KEEPS O33296, zero-diameter refusal in the engine's words with the document surviving, open two-line chain applies with QC dangling flags. Suites: test_sketch 21 controls + test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 all pass. Honest limits: typed numbers only (mouse drawing, snapping, trim/fillet later); no constraint solver by ruling; a sketch job nests/posts exactly like any other job (nothing special-cased). [2026-09-01, fix block 4: sketch UNITS PINNED to inches in the pipeline -- the R12 source.dxf carries no $INSUNITS and a >200-inch sketch was silently re-read as mm by the magnitude heuristic; pinned, never inferred, the intake note says so.] LABELED CONTROLS + EXCEL ENTRY 2026-09-06 (Jordan testing the sketcher: 'nothing is labeled. Can we improve this so that a human operator has intuitive controls?' and 'When I click a dimension box, I want the number I'm typing to automatically replace the text... Kinda like Excel'): every sketch field now carries a visible label above the input (line: from X/from Y/to X/to Y; circle: center X/center Y/Ø dia; arc: center X/center Y/radius/start°/end°/dir) -- a hover tooltip alone is not a label; and clicking ANY unit input app-wide selects its whole value so typing replaces it (focusin select + one-shot mouseup suppression so the browser keeps the selection; a second click in the focused field places the cursor normally). Client-only (app.js/app.css), serves from disk, no restart; proven by 6 live real-mouse Playwright checks on Jordan's own sketch job. DRAW GESTURES ARRIVED 2026-09-19 (Jordan's "Yes" -- pin add-feature-draw-flow carries the full record): the 'mouse drawing, snapping later' honest limit closed -- click-to-draw line chains, the draw parameter bar and magnet snaps run on the sketch preview, committing typed rows; typed forms stay the record and the fallback; arc/circle rows arm a one-click center pick. verify_sketchdraw.js 17/17 + verify_sketch 13/13 (suite-fixed for the tab merge + dev-links BASE).
- •Tear-off panels & workspaces (UI brick 11) — the A+C ruling live: dock ground state, tear-off floats, one-click snap-back, named per-station layouts, a panel never lost record — BUILT (brick 11 of the 2026-09-01 build-them-all go) — the A+C ruling (Aristide 2026-08-19: docked Workbench is the ground state on every workstation; C's machinery underneath — tear-off panels, named per-user Adobe-style workspaces — snap-back always one click) as it stands in app/web. The two panels that exist today (Tools, Checks) each carry a GRIP: dragging it IS the tear-off (proposal A's gesture) — the panel becomes a floating window over the canvas, draggable by its grip, resizable by its corner, and the dock reflows so the canvas takes the freed space; the SAME live drawer renders inside the float (one renderer, no parallel panel). Drop a dragged panel on a screen edge to dock it there (panels drag between slots — Tools can dock right, Checks left; an edge hint bar shows the target); the float button is the ruled ONE-CLICK snap-back to the panel's last slot. Collapse ('–') folds a docked panel to a 26px strip with a vertical title, one click expands (proposal A's icon strip). NEVER LOST, enforced two ways: any float clamps into the viewport on load and on window resize (a stored x=9000 comes back on-screen — verified), and Reset re-docks everything. Workspaces menu (⧉ chip): save the current layout under a name, apply, delete, Reset — layout state is per-STATION presentation state (localStorage, exactly like the ruled theme/units chips) with the honest note on-screen: per-station until operator accounts arrive. THREE-LENS CHECK APPLIED: floats live INSIDE the app window on all three lenses alike (plain DOM + pointer events); true separate-OS-window / second-monitor placement is platform-divergent on the web lens, so it is recorded as arriving with the Mac/PC shells — stated on-screen, not silently web-only. Defect caught in the build: a click inside the workspace menu that re-renders the menu detached its own click target before the close-on-outside-click handler ran, so the menu closed itself after Save — the handler now tests the event's composedPath, captured at dispatch. No server or engine change; panel INVENTORY unchanged (more panels join the dock as they are built — the MetaCam panel document is still the awaited inventory source). Verified live over the public URL (app/verify_panels.js, Playwright, 14 checks): tear-off by real mouse drag, canvas reflow, layout surviving a full reload, one-click snap-back, edge-docking, collapse/expand, save/reset/apply/delete of a named workspace, off-screen clamp, and the torn-off Checks serving the real nav209 Part review drawer. Suites: test_sketch 21 + test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass.
- •Sales surface (UI brick 12) -- the stripped quote door wired to the existing two-tier quote stack record — UI brick 12 BUILT 2026-09-01: the SALES SURFACE -- the ruled stripped flow (Aristide 2026-08-19, sitting answer 2: upload, assign material, auto-nest -> material-use + time estimates -> pricing) live in the app, WIRED to the EXISTING quote stack (2026-08-21: quote.py FIRM tier, fast_estimate.py FAST tier, rollup.py Q29 per-shop policy, rates.json) -- nothing re-implemented. Sales door on the Jobs home = a stripped VIEW of the same tool (intake/heal/qc/quote tabs only; role locks await operator accounts -- stated on-screen, never silent); a sales upload marks the job origin:sales -- the SAME job object the operator opens ('it moves the start, it doesn't move the program'). POST /api/jobs/<id>/quote: AUTO runs a defaults Part review on an untooled part and a table-values nest at qty (an existing reviewed/nested state is priced AS IS -- operator overrides survive, nothing clobbered); writes quote.nc from the reviewed plan (O1 counting artifact, audited per the 2026-08-12 rule, NO program number consumed) -> quote.count() measured per part x qty_filled -> quote.price() -> rollup.compose(). A missing shop rate = a BLOCKED line naming its exact rates.json row, fillable in the drawer (POST /api/rates writes value + provenance decided/dated -- 'This is today's rate'); seed rows keep the composed figure honestly INDICATIVE with assumed_rows named; markup/margin stay per-shop policy knobs, never auto-picked. FAST tier beside the firm one, biased at-or-above (15d). Customer approval = the ruled explicit gate between sales intake and production finishing, recorded by typed name until the portal binds identity. Staleness: re-plan, re-nest or sketch re-compile marks the quote stale on-screen; the document STANDS (a quote is a record). TWO DEFECTS CAUGHT IN THE BUILD: (1) the client split the dotted rate row on '.' -- a material key carries a dot (MSO7,0.125), so a typed rate landed in a junk row; material-keyed sections now rejoin the key; (2) fast_estimate.measure() built its bbox from vertex points only -- a pure-circle part (two semicircle bulges) measured bbox height ZERO, zeroed the sheet estimate and the fast tier read BELOW the firm tier, the exact 15d failure: arc axis-extremes now ride into the bbox and sheets_high floors at 1 (verified fast $258.66 >= firm $257). Verified live over the public URL: app/verify_quote.js, Playwright, 14 checks (sales door, origin:sales, stripped tabs, blocked-rows worksheet, typed rate re-prices with provenance, INDICATIVE total, fast>=firm, approval record, stale mark, full station same job); rates.json restored after the verify -- throwaway figures never linger as decided shop values. Suites: test_sketch 21 + test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass.
- •First-run tour, help from anywhere, bug report (UI brick 13) -- sitting answer 9 live record — UI brick 13 BUILT 2026-09-01: FIRST-RUN TOUR + HELP FROM ANYWHERE + BUG REPORT -- the ruled scope of sitting answer 9 (Aristide 2026-08-19) live, nothing more. (1) GUIDED TOUR: a detectable first run (per STATION via localStorage, like theme/units -- per-user first-LOGIN detection arrives with operator accounts, stated on the first card) auto-starts a 10-step tour of the whole gamut -- where things live (Jobs home / dropzone / sales door), how tools open (one-letter keys), how a panel tears off (the step spotlights the real grip: 'try it now, or Next' -- the overlay passes pointer events, only the card takes them, so try-it works LIVE under the tour). Skippable per step (Next never forces an action) or Skip all; Escape ends it; replays any time from the ? Help menu. Ruling 6 rides in it: the dot/colour vocabulary (colour AND glyph -- done/flagged/blocked/pending, warn-never-block, refusals in the engine's own words) is TAUGHT in the tour, once, with a live legend. Steps anchor real on-screen elements through a spotlight hole; an anchor that needs an open job (tabs, canvas, zoom) falls back to a centered card at the Jobs home rather than pointing at nothing. (2) HELP FROM ANYWHERE: a ? chip in the always-present topbar -- guided-tour replay, Manual (HONEST: not written yet; the tour and tooltips are today's teaching surface), Report a bug. (3) BUG REPORT FROM ANYWHERE, capturing what is on screen: canvas as PNG + app state (job, tab, stage statuses, units, viewport, UA) -> POST /api/bugreport -> one immutable directory per report under app/bugreports/. Routing to the partners' existing bug server AWAITS the details Aristide supplies, and the fuller auto-capture list is specified together later -- both ruled-pending, both stated on the form itself. DEFECT CAUGHT IN THE BUILD: the bug modal's display:flex outranked the UA's [hidden]{display:none} -- the INVISIBLE modal covered the screen and ate every click; [hidden] override added, named in the verify. Verified live over the public URL (app/verify_tour.js, Playwright, 20 checks, fresh context = fresh station): auto-start, per-station wording, non-blocking overlay, anchored spotlights (dropzone/tabs/grip), taught vocabulary, Skip all persistence across reload, honest Manual, replay+Escape, bug report verified ON DISK (report.json words+state, screen.png beside it). Suites: test_sketch 21 + test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass. Tour != onboarding wizard: the wizard (shop-archetype profiling, common-line question) is its OWN pinned intent, untouched by this brick.
- •Multi-sheet programs -- one program per sheet (UI brick 14) record — UI brick 14 BUILT 2026-09-01: MULTI-SHEET PROGRAMS -- ONE PROGRAM PER SHEET, the recorded honest limit of bricks 8/9 closed. SERVER run_postnest loops EVERY sheet of the persisted nest layout: each sheet posts its own complete program (macro or part-by-part -- the two-shapes ruling unchanged, Manual Sort drops the macros on every sheet alike) with its OWN O-number from the one shop sequence (nest-name-prog-number: sheet 1 takes the operator's typed number; EVERY sheet keeps its number across re-posts AND re-nests -- the stage record carries onums[], _drop_postnest deletes every per-sheet artifact keeping all numbers). Each sheet's program is audited by nc_warn.audit_and_inject against ITS OWN placed geometry, callouts riding in its own file. Files: postnest.nc (sheet 1, the bricks-8/9 name -- old readers keep working) + postnest_s<k>.nc, each with its per-sheet tooling doc and slug report; postnest.json keeps sheet 1's program at top level and adds programs[] for all sheets. DELIVERY run_delivernest ships EVERY sheet: per-sheet O<num>.nc + census-checked MetaCam-safe sheet DXF (a FAIL on any sheet refuses the whole delivery) + tooling doc/slugs, ONE placement->part map for the whole job whose sheet+program columns name which program cuts each placement (the 2026-08-31 map ruling extended), packet with per-sheet program lines and usage measured over ALL sheets, per-sheet SVG previews, all in one zip/version/board build. UI: the Post tab renders every sheet's program (number/form/arrays/boundaries/warnings/downloads per sheet + the one-program-per-sheet note at the form); the Deliver tab lists every program in the version row and the chain. Verified live over the public URL (app/verify_multisheet.js, Playwright, 15 checks): a 30x40 sketch part at qty 5 spills 3+2 over two sheets -> two programs with distinct auto-sequence numbers, per-sheet audits (6 vs 4 executed boundaries), numbers kept across re-post AND re-nest->re-post, delivery with both NCs + both DXF censuses PASS + the sheet-aware part map + packet lines, and the single-sheet nav209 document still rendering (regression). Suites: test_sketch 21 + test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 pass. Honest limit: customer sheet later brick (nest-level part sequence BUILT as brick 15, ui-nestseq-brick15).
- •Nest-level part sequence (UI brick 15) — serpentine default CONSUMABLE, numbered boxes, click-in-order at part scale record — BUILT (brick 15 of the 2026-09-01 build-them-all go): the ruled scope of pin nest-part-sequence (Jordan 2026-08-28 walkthrough step 3) live and CONSUMABLE. ENGINE nest_rules.serpentine_order(placements): the ruled default order -- bottom-left start, up column 1, down column 2 (Jordan's live 3x18 nest) -- columns grouped by X within tol; test_nestseq.py proves the worst consecutive hop is one column pitch where the all-up emission order pays the full column height (the rule's whole point). SERVER: GET/POST /api/jobs/<id>/nestseq -- the numbered-boxes state: engine serpentine per sheet + the operator's clicked order, ONE source (the UI never recomputes the serpentine). Saving a clicked order validates every [sheet, placement] entry (bad sheet / double click refused in the server's own words -- a plain request error, never a blocked stage) and drops the posted nest program per the stale-program rule, every sheet keeping its O-number; clearing restores the serpentine + macro eligibility the same way; a re-nest clears the sequence naturally (it lives ON the nest document -- new placements, new numbers). run_postnest consumes it: EITHER override (per-feature Manual Sort or nest-level clicks) drops the P9996/P9997 macros per the two-shapes ruling -- a part-by-part program runs its parts clicked-first-then-serpentine (default pure serpentine, source rule:serpentine); each program records part_order + part_order_source, macro mode honestly naming the control's own G65P9997 array loop as its order. The operator's order rides the same audit as every post (nc_warn.audit_and_inject vs placed geometry -- same shared rules, warnings ride into the NC; moves the ORDER, never a gate). UI Nest tab: numbered boxes on every placed part (numbers restart per sheet -- each sheet is its own program), 'Click parts in order...' mode with add/unpick canvas clicks and live renumbering, the macro cost notice at the FIRST DEPARTING CLICK (a serpentine-prefix order keeps the macros), Apply / Cancel / Back to serpentine; Post tab names the part order per program. DEFECT CAUGHT IN THE BUILD: a #tab=nest deep link part-fit the view and left the sheet off-canvas (openJob always fit the part) -- deep links onto sheet tabs now fitsheet exactly like clicking the tab. Verified live over the public URL (app/verify_nestseq.js, Playwright, 17 checks, fresh page per phase): serpentine on a 5+5+2 layout, macro default unchanged, departing click -> notice, clicked order persisted, macro -> part-by-part -> macro with O33307 kept throughout, part_order [5,0,1,2,3,4,9,8,7,6,10,11] exact, refusals verbatim, nav209 196-permutation + ms-verify multi-sheet regressions. Suites: test_sketch 21 + test_drag 13 + test_tooling_doc 21 + test_rules 11 + test_nc_warn 12 + test_nestseq 12 (new) pass. Open with Jordan (unchanged from the intent pin): 'Process in part-by-part mode' checkbox semantics, 'Process only one part of each type'.
- •User strings are TEXT on every rendered surface (systems-test fix block 1) record — Every user-typed string (part name first) is HTML-escaped at the render boundary on every generated page and every app innerHTML site; the census exemption is scoped to the marked name spans; the part-map CSV quotes a name that needs it.
- •App robustness: honest 404s, request validation at the door, atomic writes + locks (systems-test fix block 2) record — A malformed request is a plain 400 in honest words and touches no job document; a missing job is an honest 404; every JSON document writes atomically; a per-job lock and a progseq lock kill the measured races (duplicate O-numbers, deliver version-dir collision, torn reads); same-second job ids and ghost job dirs fixed.
- •Program-number override modes: use once (default) vs start new sequence; reserved/used = yellow, never block (systems-test fix block 3) record — Ruling B (Aristide, 2026-09-01) built: an operator program number is USED ONCE by default -- the shop sequence does not move, so a one-off O99999999999 can never poison it -- or restarts the sequence at that number when the operator picks NEW SEQUENCE. A reserved or apparently-used number posts anyway with a YELLOW warning that rides in the record, the stage counts and the NC file itself as a top-of-file callout; the auto-increment never silently re-issues a use-once number ahead of the sequence.
- •Stage-level yellow warns: machine envelope, sub-beam-comp features, units inferred + sketch units pinned to inches (systems-test fix block 4) record — Fix block 4 (2026-09-01, Jordan's 'Go'): three yellow warns the systems test found missing, all warn-never-block, riding the block-3 stage-warning path. (1) MACHINE ENVELOPE (Jordan 2026-08-24 'flag anything, part or nest, that exceeds the job's machine'): a part over the bed in either orientation warns at Part review and the callout rides in every posted NC. (2) SUB-BEAM-COMP FEATURES: a contour whose smallest dimension is at or under TWICE the row's beam compensation warns at review and in the file -- severity PROPOSED yellow, on Jordan's ratification list. (3) UNITS INFERRED: a headerless upload flags intake with 'units were INFERRED, not stated' in honest words; a file stating $INSUNITS stays clean. Plus SKETCH UNITS PINNED: sketch-born geometry is typed in inches by definition -- the pipeline pins it and a >200-inch sketch is no longer silently re-read as mm.
- •Brick 17: warning rows NAVIGATE — click a finding, land on its geometry, pulsed; wordmark/job-name return to Jobs home record — Built 2026-09-01 on Aristide's 'go' after his review of the Part-review yellows ('Quite frankly, I do not see them'): the click existed but gave invisible feedback — selectContour only highlighted the contour in place and swapped the drawer; the view never moved, rows didn't look clickable. Now every finding row that KNOWS its geometry (bnds -> contour, an '(X.. Y..)' in the shared message string -> the exact point) renders as navigable — pointer cursor, accent hover ring, a go-glyph suffix — and the click selects the contour AND frames the spot (zoom capped at 800% so a tiny hole never becomes a wall of pixels) with a ~1s expanding pulse ring at the exact point, so the operator's eye lands where the warning fired. One renderer (findrow()) across Part review findings+warnings, Cut plan findings, Post single-part findings + stage warnings, and the nested-sheet section's stage warnings. Stage-level warnings with no geometry (units-INFERRED, envelope) render flat — honestly not navigable; the nested program's expanded-boundary findings stay flat too (renumbered bnds don't map to part contours). Home navigation made visible: the kerfmaster wordmark and the job name both return to the Jobs screen (shared goHome(); underline on hover) — the path existed on the job name but nothing looked clickable. Warn-never-block untouched: navigation reads findings, changes nothing. Verified live over the public URL (app/verify_warnnav.js, Playwright, 10 checks, fresh page per phase): review click zoomed 103.3%->509.7% onto the clear-air hole and opened its contour record, cut plan + post navigate, units-INFERRED stays flat, wordmark + job-name land on Jobs home. UI-only change — no server/engine edits, python suites unaffected. This is brick A of the 2026-09-01 execution plan; next: B drag-handle UI, C consumable tab size, D propagation ALL/SINGLE, E part-list nest UI, F Heal/QC manual edit buttons.
- •Brick 18: the drag-handle — grab the pierce ring, drag along the contour, the engine judges every candidate LIVE, the drop applies the override record — Built 2026-09-01 on Aristide's 'B' (brick B of the ruled execution plan) — the UI gesture over the finished engine seam (pin drag-handle-validator): Jordan's Add-Toolpath ruling live, pierce + lead + start move as ONE handle. SERVER: POST /api/jobs/<id>/dragcheck — rules.check_drag against the assigned Part review's material x machine (refusal in honest words before a review exists), read-only by construction: no job lock, no stage record, nothing persisted; the override shaping is ONE function (_feats_from) shared with run_review so the live judge and the recorded judgment can never read an override differently, and an empty drag returns byte-identically the review's own findings (no drift, proven live). UI (Part review canvas): the pierce ring is grabbable (grab cursor on hover, 10px hit) — mousedown grabs (selects the contour), the handle rides the cursor SNAPPED to its contour's as-cut path, throttled (~7/s) dragcheck calls colour the handle red/yellow/green by what WOULD fire there with the counts written beside it ('2 yellow would fire here' / 'clear here'); mouseup applies start via the SAME override channel as pick-on-canvas (S.ov near+start -> review overrides= -> source:operator) and re-plans — one plan, one judgment; Escape cancels with nothing recorded; empty-canvas drag still pans. Warn never block throughout; the posted-NC re-read stays the final gate. Concentric-contour lesson recorded in the suite: two contours sharing a centroid tie under nearest-centroid binding — inherent to the binding, fixtures must offset. Proven: test_dragcheck.py NEW (15 controls: refusal, no-drift, outer-first candidate fires the shared red live before any NC, read-only byte-stability, malformed 400s) + app/verify_draghandle.js live over the public URL (Playwright real-mouse, 13 checks: grab cursor, live judgment mid-drag, drop -> operator override recorded -> cleared/restored, Escape cancels, endpoint persists nothing, pan regression) + test_robust 50 / test_warns 17 / test_sketch 21 / test_drag 13 / test_tooling_doc 21 / test_rules 11 all green after the _feats_from refactor. Honest limits: the handle forces the START point — lead length rides the existing typed slot (the drag does not stretch the lead); wirejoint-rides-the-drag is engine behavior at the new start, not a separately grabbable tab handle yet. Next per the plan: C consumable tab size, D propagation ALL/SINGLE, E part-list nest UI, F Heal/QC manual buttons. EXTENDED 2026-09-17 (Jordan's 'the click and drag ... would magnet to midpoints' catch, his 'Go'): the handle now magnets to corners / edge midpoints / quarter points (quadrants+45s on circles), Alt slides free -- full record on pin start-anchor-constraint. FIXED same day (Jordan on prod: 'still free-moving with no magnet'): the pull was measured from the RAW CURSOR, but the handle rides the contour by projection -- a hand drifting >10px off the edge (the natural sliding motion) never triggered a magnet; measured 0/16 landmarks caught at a 20px perpendicular offset. The pull now measures from the PROJECTED handle point (cursor kept as a secondary catch): 16/16 at the same offset. Landmark dots raised 2px->3px at 0.8 alpha so the operator can see them. Client-only. MAGNET DERIVATION REBUILT ON TRUE PRIMITIVES 2026-09-18 (Jordan's Magnets_9-18-1 screenshot -- red dots at the mid/quarter points, rad endpoints and rad 45s he expected, none of them magnets; Aristide 'go for fix'): the built derivation detected corners by a 25-degree turn test on the sampled as-cut poly, so a TANGENT corner rad -- which never turns sharply anywhere -- yielded no corners at all and the whole loop degraded to the 8-point smooth-circle fallback (the compass extremes near the rads were what read as 'endpoint' magnets; the pull at the lead-in was just the handle's own contour projection). The defined rule (corners/midpoints/quarters, quadrants+45s on circles) stood; the implementation missed it on tangent parts. NEW dragMagsSegs derives landmarks from the plan row's TRUE as-cut segs: units first -- collinear line fragments merge into one edge, contiguous pieces of the same arc merge into one sweep (across the cut-start wrap too, so a start point mid-edge invents no corner) -- then every unit endpoint is a corner landmark (TANGENT seams included: both ends of every rad magnet even at zero turn; tab-gap ends too), straight edges >= 0.08 get midpoint + quarters of the WHOLE merged edge, arcs get their 45-degree bearing marks inside the sweep plus the arc midpoint (a 90-degree rad's midpoint IS its 45; a rotated rad that misses every 45 bearing keeps its natural mark), full circles = quadrants + 45s unchanged. Reviews that predate plan segs keep the old poly turn test as fallback. The start-anchor corner pick rides the same function, so 'the bottom right corner' now also means a tangent seam. Client-only (app.js). Proven: 12-control node unit test on the derivation (rounded rect with a dirty-CAD split edge, full circle, off-45 rotated rad, cut-start-mid-edge wrap) + LIVE on dev 44558B_10's outer: 10 corners = all 8 true tangent-rad seams + the 2 tab-gap ends with the one artificial same-arc split correctly erased, 19 pts = mid/quarters per edge + a mark on every rad -- exactly Jordan's red-dot set; verify_snapdrag 10/10 + verify_draghandle 13/13 + verify_gc56 25/25 green.
- •Brick 19: tab size as a per-contour CONSUMABLE — typed width per feature, 0 = no tab (suppress-wirejoint), width moves, gates don't record — Built 2026-09-01 on Aristide's 'c' (brick C of the ruled execution plan): tab_width joined tooling_doc.CONSUMABLE in the same commit as the engine input, per the model rule — the walkthrough's typed tab size live end to end.
- •Brick 20: identical-feature propagation — mode ALL vs SINGLE FEATURE, the engine's own congruence record, one override channel record — Built 2026-09-01 on Aristide's 'd' (brick D of the ruled execution plan): Jordan's walkthrough ruling live — manipulate ONE feature and 'the rest all followed suit', vs SINGLE FEATURE; groups come from the engine's record, never re-derived by a lens.
- •Brick 21: the part-list nest UI — Qty / Rotate / Bridge / Filler live over the proven pack engine; 2026-09-20 preview cards -- thumbnail + full name + X x Y per row, selected part large on canvas, sheet moved to the Nest tab record — Built 2026-09-01 on Aristide's 'E' (brick E of the ruled execution plan): the part-list part-by-part surface — rows reference healed jobs, the four ruled columns, pack through part_list_nest.pack. Synthetic fixtures; Jordan's real test job is the first real fixture and drops straight in. BACK-TO-PART-LIST CHIP 2026-09-14 (Jordan stuck mid-list: a row's 'open' button swapped the screen to the part's own job with no way back, and DRAFT rows -- client-side until 'Save list + pack sheets' -- died on the trip): KM.openPart now stashes the list job + draft rows + machine/material picks; a '← back to <list>' chip rides the topbar on the opened part, one click returns AND restores the unsaved draft; chip clears on return or going home. Client-only app.js, live immediately; verify_plreturn.js 11 live real-mouse checks (draft row survives the round trip, zero page errors) on the Lathrup list, read-only. ROW LABELS SHOW THE TAIL 2026-09-17 (Jordan's Detroit Wrecker catch -- six uploads sharing the 22-char filename head 'DWS-LH-2.5-LOGO-TREAD-' all clipped to the identical prefix in the 150px row select, so 'where are the files I uploaded?' -- they were all six rows, indistinguishable): the part-list rows now apply the operator-PDF _short_names rule client-side -- when listed rows share a substantial (>=8 char) filename prefix with every tail non-empty, option labels carrying that prefix display the tail, a drawernote states the stripped prefix, and the full name rides each row's tooltip; a row's job change re-renders so prefix and tooltip stay honest. Display only -- rows/pack/record unchanged. EXTENDED 2026-09-20 (Jordan's Part-List screenshot -- 'It is difficult for me as the operator, just now seeing these parts for the first time, to know which part is which'; his 'Go' on the numbered proposal): the Part list tab is a LIST, not a sheet -- every row is a preview CARD: an outline thumbnail of the actual part, the full name (shared-prefix tail rule kept, full name on the card when shortened), a read-only overall X x Y bounding size (as drawn; the tooltip states a quarter rotation lets the pack stand it up), and the same recorded inputs the packer consumes (qty, rotate step, bridge, filler). Clicking a card shows that part LARGE on the canvas, named and dimensioned -- the packed sheet stops drawing on this tab and lives on the Nest/Post/Deliver tabs where the programs post from. ONE geometry builder (plPathFromGeo) feeds the nest canvas, the thumbnails and the preview; the pack's own geometry fetch seeds the row cache so a packed list previews instantly. Presentation and navigation only: rows keep their data-pl hooks, the saved list and packed nests are untouched.
- •Brick 22: Heal/QC manual edit buttons — the MetaCam Basic-tab block as the operator's per-case override record — Built 2026-09-01 on Aristide's 'F' (brick F of the ruled execution plan): the Basic-tab edit/QC block live — five read-only mark lenses + Join / Fit Arc / Remove Small Segments as recorded, replayed operator edits in a third dockable panel. Jordan's ruling applied: automation heals first, every button has a home. NO-OP GUARD 2026-09-06 (the 44558B_10 layout drop): a healedits save that leaves the list UNCHANGED -- Clear all with nothing recorded, or an identical re-save -- is now RECORD-ONLY (noop:true, honest note, nothing re-runs, no stage drops; the units-confirm precedent: never drop stages over a no-op). A changed list still re-heals and the standing stale rule still fires. The real incident: an empty Clear all re-ran the pipeline and dropped Jordan's hand-edited 66-placement nest minutes after delivery; restored byte-exact from the auto-versioned record (review/nest/cutplan/postnest + job stages merged, v3 delivery kept). Proven: test_healedit_noop.py 8 NEW server controls; healedit 29 / nestdeliver_rot 14 / nestedit 38 / units_confirm 27 / sketch 21 green. EXTENDED 2026-09-10 (Jordan's go): DRAG-TO-SNAP -- engine op snap_end drags the loose end nearest the grab along its OWN carrier (a line extends/trims on its own direction, an arc end slides on its own circle -- center+radius rigid, the sketcher's rule); the drop lands ON the carrier, off-curve misses reported in the note; reach-gated by point like delete (replay skips in the same words when the end left). UI: 'Drag end to point' armed mode -- loose ends ring as grabbable, the handle rides its carrier under the cursor, the aim SNAPS to corners/points of the gray original base layer (accent ring), Escape cancels, the drop records through the standing /healedits door. Proven: test_healedit 60->70 + verify_snapdrag.js 10 live real-mouse checks (snap to (0,0) exact, QC closes, persistence, refusals verbatim, Escape). EXTENDED 2026-09-15 (Jordan's standing go, Aristide 'go ahead and start': BAND SELECT ON THE GEOMETRY TAB -- the committed next build built): Illustrator-style rubber band over the healed layer. ENGINE op delete_seg = the per-segment delete (takes the ONE segment nearest the point, never the chain -- delete stays the chain op, the two distinct on purpose); same reach gate/replay-skip words as delete, removed ink rides the report row (fidelity exemption), last-geometry refuses ('nothing would be left to cut'). UI: 'Band select' armed mode in the Heal/QC panel -- drag a box, segments select by sampled on-curve points + anchor points (endpoints) inside the rect (selection colored --sel, anchors as squares: filled = banded point, hollow = selected segment's ends); Shift-band ADDS; a tiny drag = click clears; Escape clears first, disarms second; Delete/Backspace removes every selected segment in ONE save -- one delete_seg row per segment (near = a point ON its own segment), per-row removable in the recorded list, one re-heal, standing stale rule; a GRABBED LOOSE END rides the SAME drag-to-snap with no mode switch (the marked rings are shared). Selection is a picking aid only -- the engine re-finds every segment by point behind its own reach gate. Scene-guarded like armed clicks (never over a nest scene). TDZ lesson re-hit and fixed (top-level KM.x= before KM initializes -- plain function now, the plhRenderBar precedent). Proven: test_healedit 70->78 + verify_bandselgeo.js 13 NEW live real-mouse checks (band 2+4 anchors, shift-add, escape ladder, grab-rides-snap, one-gesture delete, delete-everything refusal verbatim, fresh-page replay, per-row restore); healadd 13 / healedit_noop 8 / sketch 21 / isoedit 18 / isoserver 17 / split_door 13 / dragcheck 15 green; verify_snapdrag 10 re-run green.
- •Brick 23: the onboarding wizard + shop profile (GET/POST /api/shop) record — Built 2026-09-01 on Aristide's 'go for next build': the ruled first half of the common-line item — the shop-setup wizard live, SHOP-level shop.json shared by every station, the two ruled questions (archetype, common-line yes/no), seed-never-bind, 11 live checks. TABLE REFUSALS POINT HOME 2026-09-10 (Jordan's ruling on the table-less flow -- 'I would just like to see a simple message saying "for this to work we need more information from you" and then a link back to the wizard'): every stage BLOCKED by a TABLE gap (tables.TableError incl. MaterialUnknown/MaterialIncomplete/RateMissing, plus the Rev2 no-row/blank-cell KeyErrors -- marked needs_setup on the stage record by the one server catch) now leads with exactly that plain sentence + an 'Open Shop setup...' link, on every surface through ONE renderer (blockBanner -- 12 call sites folded); the engine's exact words stay beneath, never hidden -- they name the row and column to fill. The wizard gained an honest Machines & materials section: defining them in-app is a coming build, today tables load from the shop workbook and quote rates fill on the Quote drawer; geometry work (heal/edits/clean-DXF delivery) needs no tables at all. Proven live: app/verify_setupask.js 8 Playwright checks (scratch sketch -> Stainless row without materials.json backing -> blocked + needs_setup + banner wording verbatim + link opens the wizard + honest note; fixtures removed). HUMAN SPEAK 2026-09-10 (Jordan: 'use human speak, not computer speak... "Material Not Defined" "Material Thickness Not Entered"'): a needs_setup refusal now LEADS with plain-words heads -- Material Not Defined / Material Value Not Entered / Machine Settings Not Entered / Shop Rate Not Entered -- with the missing thing named in a human sentence (client humanTableWords over the engine strings, presentation only); the exact table words stay one click away in a collapsed 'exact table words' row, never leading. verify_setupask.js 8->10 checks. TOAST JOINS THE TREATMENT 2026-09-16 (Jordan's CAMP 0.040 pack refusal toast showed the raw MaterialUnknown words -- the part-list pack-refused toast bypassed humanTableWords while the blocked-tab banner translated): the toast now leads with the same human head/sentence and points at the Nest tab banner for the exact table words. Client-only, live on refresh.
- •Brick 24: common-line cutting — the shared-edge web design, engine + Nest-tab disclosure block record — Built 2026-09-01 on Aristide's 'go': common-line cutting's first working form — common_line.py answers the shared-edge safety design the pin left open (perimeter-last, monotone band sweep, no-comp shared edges), plans the welded-band layout + ordered cut plan with the trade in numbers, and the ruled default-disclosure block is live on the Nest tab reading the shop profile. Posting the shared-edge program is the next brick.
- •Brick 25: posting the shared-edge web — the common-line program, one per sheet record — Built 2026-09-02 on Aristide's 'go': the recorded brick-24 limit closed — the common-line plan posts as real Fanuc macro-B programs, one per sheet. cl_post.py: every closed cut (holes, band perimeters) posted by write_gcode_macrob itself, one welded band per call (outer-last IS perimeter-last — zero drift on leads/comp/slug tabs/chording); shared edges written as open NO-COMP cuts, pierce and lead in the inter-band channel, overcut past the far perimeter; its own re-read audit judges the stitched file sheet-wide, callouts ride in the NC.
- •Brick 26: Traverse Routing — rapids detour around held slugs record — Built 2026-09-02 on Aristide's 'go': the recorded unbuilt rapid-routing lever (MetaCam's Traverse Routing) — approach rapids detour around pieces already cut and still at bed level; a crossing no detour can clear stays straight and counted, never silent.
- •Brick 27: the Dimension surface — engine-measured dimensions on the canvas record — Built 2026-09-02 on Aristide's '1st one': the metacam-dimension-tab-menu intent's first working form — a fourth dockable Dimension panel; H/V/aligned/radius/diameter/angular picks snapped and MEASURED by the engine (a readout, never an input), Auto dimension, live re-measure on every geometry edit, stale in the engine's words. DRAG-TO-PLACE 2026-09-09 (Jordan 'do this'): grab the value label, drop it where you want it -- presentation only, the value keeps re-measuring. ARMED-GRAB FIX 2026-09-11 (Aristide: after two armed picks placed the dim, 'shouldn't it automatically flip so I can now move the dimension?' -- Escape was needed first): the kind stays armed on purpose (place several dims in a row without re-clicking the button), but the label grab now WINS over the pick whenever no pick is mid-flight -- drag right after the dim lands, no Escape; a pick sequence in progress keeps every click a pick, and the armed crosshair no longer clears on mouse move. Client-only, live immediately; verify_dimarm.js 12 live real-mouse checks. AIM FEEDBACK 2026-09-11 (Aristide: hovering should 'magnetically attach' and leave a 'trail'; 'pick 1 of 2' in the button 'took me a few days to discover'): with a kind armed the canvas shows the live aim -- a magnet marker at the point the pick will snap to (client mirror of the engine's dims.snap: vertex / arc center within the 0.25 reach, else edge foot -- a picking aid only, the raw click still posts and the engine stays the authority), a hint chip riding the cursor ('pick k of n -- corner / circle center / on edge'; honest no-geometry-in-reach words in warn color when the click would refuse), markers on picks already taken and a dashed rubber-band trail from them to the aim; suppressed over a grabbable label, cleared on pan / Escape / disarm / leaving the canvas. Client-only, live immediately; verify_dimaim.js 12 live real-mouse checks + dimarm 12 / dimdrag 10 re-run green.
- •Brick 28: toolpath-after-nest for part lists — lead/pierce envelopes reserved in the pack record — Built 2026-09-02 on Aristide's 'go with what you have': the brick-21 honest limit closed — part-by-part tools AFTER the nest, and the pack reserves the room.
- •Brick 29: posting the part-list sheet program — one program per sheet, part-by-part by construction record — Built 2026-09-02 on Aristide's 'Go': the remaining brick-21 honest limit closed — the packed part list posts as real Fanuc macro-B programs, one per sheet, part-by-part by construction (several unique shapes, no macros); per-part overrides join part-list plans; plans_match is the stale gate. (Form superseded 2026-09-17: main + M98 sub callouts -- pin pl-post-subcall.)
- •Part-list programs post as MAIN + SUB-PROGRAM CALLOUTS (G52 + M98P, one sub per distinct part x orientation) -- and MACRO RUNS where the layout earns them (P9997 arrays, style auto/partbypart) record — Built 2026-09-17 on Jordan's GO (his O33342 catch: 'Where is the main program as we've discussed?'): the 2026-09-06 G52 sub-call ruling lands on the part-list surface -- edit one sub and every copy follows. EXTENDED 2026-09-19 (Jordan 'Go' on the standing macro-run offer): where the placed layout forms uniform runs, style auto posts the true-nest macro form -- one G172 + G65P9997 per run, a lone placement as K1Q1, never a mixed main. EXTENDED 2026-09-20 (Jordan's 26342 catch: 'we should put these values as what they would have been if there were repeated parts in X or Y, rather than leaving them at 0 ... they can easily call that up in the gcode without having to do math on the fly'; his refinement: an overridden clearance must show in the words; his 'Go if this makes sense'): SPARE PITCH WORDS on single-count axes -- every K1/Q1 axis posts the pitch a repeated run WOULD use, so a make-up part in the scrap is one K or Q edit at the control.
- •Brick 30: the part-list delivery — versioned set, floor packet, placement→part map, customer sheet record — Built 2026-09-02 on Aristide's 'next natural brick': the posted part-list sheet programs become a versioned immutable delivery set — per-sheet audited NC + census-checked MetaCam-safe sheet DXF, one placement→part-number map naming each placement's OWN part, floor packet with asked-beside-filled per row, customer sheet under every brick-16 split rule (multi-name exemption), zip + numbered board build; append-only chain, supersede as metadata, replanned marker on list edit/re-pack. EXTENDED 2026-09-17 (Jordan's 'I changed the program number to 33385... and it reverted to 33342' -- his re-post DID take (plpost.nc = O33385, operator source recorded) but he downloaded from delivered v1, which immutably carries the OLD O33342 program; nothing on the Deliver tab said a newer post existed): the part-list Deliver tab now shows a warning note whenever the posted program is NEWER than the latest delivered version (stage plpost.date vs the version's own date; both O-number sets named) -- 'the vN downloads below are the OLD post, click Deliver again to ship the new program'. Client-only (app.js + one .drawernote.stale css rule); delivered versions stay immutable, the notice is presentation.
- •Brick 31: nest-level click-in-order for part lists — the numbered boxes on the part-by-part kind record — Built 2026-09-02 on Aristide's 'Go': the brick-15 numbered-boxes/click-in-order model lands on the part-list Nest canvas — clicked placements cut first, the pack's fill order continues after; part-by-part by construction, so the order changes the order, never the program form.
- •Jobs home filing: customer = the master file, workflow = derived queues ruling — Jordan + Aristide 2026-09-02: the program number severs the customer link at archive time — so the CUSTOMER is the master folder, workflow queues populate themselves, and the order reference is free text (a PO or 'Jim said run.').
- •Brick 32: customer registry + folders + search — filing brick A record — Built 2026-09-02 on Jordan's go: customers.json registry, POST /api/jobs/<id>/file (customer + free-text order ref), Jobs home grouped into collapsible customer folders with search over names, customers, order refs and every program number.
- •Brick 33: drawing-sheet intake -- extract the 1:1 flat pattern in the app record — Built 2026-09-02 on Jordan's go ('Go ahead for that brick 1:4'): a customer DXF that is a scaled DRAWING SHEET (title block, dims, views at 1:4/1:5/...) is detected at intake and the operator extracts the 1:1 flat by stating the print's flat size + scale -- the Lathrup preprocessing, generalized into the app.
- •Brick 34: import rotation -- MetaCam's 'Rotate Drawing' setting + open-part doors record — Built 2026-09-02 on Aristide's go, from Jordan's Import Settings screenshot: shop default 'rotate if taller than wider' stands every import's length on X; per-job override wins; part-list rows and nest placements open the referenced part.
- •Brick 35: the MASTER rotation option on a part list record — Built 2026-09-02 on Jordan's ask ('not more clicks than I want tonight, it's more clicks than I want ever'): one 'part frames' control on the Part list tab applies an import-rotation choice to every listed part; parts already in frame stay untouched; each part stays individually overridable.
- •Brick 36: Spread Y on part-list nests (+ hand-layout bridge fix 2026-09-19) record — Built 2026-09-03 (Aristide 'Go', on Jordan's 'my next step would be Spread Y which I do not see in this part of the UI'): the ruled one-click gap evening lands on the part-list Nest screen -- per column, edges included, bridges respected, meshed groups rigid, X untouched; gated by the pack's own audit.
BRIDGE FIX 2026-09-19 (Jordan's Spread_Y_9-19-2 screenshot -- 'Spread refused: KeyError: bridge' on his hand-arranged LOGO-TREAD nest; his '2. Go.'): run_plhand persisted hand-edited placements WITHOUT their bridge ({id,x,y,rot,filler} only), while every layout reader expects the pack's own placement shape -- run_plspread and run_plrefresh both read p['bridge'] and pl_spread.spread_y ranks units by it, so Spread Y and Refresh envelopes CRASHED on any hand-edited part-list layout, the raw KeyError surfacing as the refusal toast. ONE helper server._pl_fill_bridges(pn): resolves each missing bridge from the pack report's own bridges table (gap seed the last resort -- the variant door's fallback), in place. Wired three ways: run_plhand fills before saving (new hand layouts carry bridge like packed ones), run_plspread and run_plrefresh fill after loading (pre-fix hand layouts already on disk -- Jordan's job -- spread fine and the next save heals the record). Answer to his question recorded: Spread Y is a one-shot evening of the layout as it stands, never a sticky mode -- a hand edit replaces the spread layout, clicking Spread Y again re-evens the edited layout at the ruled bridges, Pack again restores the packed form. Proven: NEW app/verify_srcdl.js regression checks (hand save carries bridge 4/4, spread 200 where it crashed, refresh 200) + plhand 18, plrefresh 16, refusal 16, pltabs 19, pldock 14 green; live API proof on a stripped pre-fix layout (spread 200, record healed on disk).
- •Brick 37: consolidation -- trailing columns move into later sheets' envelopes record — Built 2026-09-03 (Jordan's circled part on the Lathrup nest, 'Are we able to try a new nesting strategy?'): the pack itself now moves whole trailing columns off an earlier sheet's clean remnant slab into a later sheet's existing envelope -- accepted only when the target's used X does not grow (free material by construction), all-or-nothing per column, reported never silent.
- •Lead-aim consistency: circles at the clock, per-orientation part-list planning record — Built 2026-09-03 (Jordan's 'Why are the lead-ins facing random directions?' screenshots, his 'Go'): a TRUE CIRCLE takes the ruled clock start outright (candidate noise can no longer aim identical holes at random), the clear-air tie-break is rounded to a thou like every other tier, and part-list nests plan the toolpath PER ORIENTATION so a flipped placement reserves, draws and posts leads that read one way in the SHEET frame.
- •Spread Y: partial columns cap at the full-column rhythm, leftover to the top record — Built 2026-09-04 (Jordan's sheet-10 catch: a 2-part column 'evened' at 11.81" gaps scattered its parts up the sheet and shredded the drop -- 'the 2 parts eating up the last remnant would not be a popular choice'; his 'Sounds good, let's do that'): a column with fewer units than the sheet's fullest caps its bottom and between gaps at the largest gap among the max-unit columns; all leftover accrues to the TOP edge as one clean recoverable rectangle.
- •Slug tabs size from the Rev2 row on every app post -- the blanket 0.018 fallback closed record — Built 2026-09-04 (Jordan's toolpath screenshot: '.018" slug retention tab where there shouldn't be one. Why is it there to begin with?' -- 'Something is broken, and it needs to be fixed'): the app's posts handed the engine tooling= but never a slug sizing table, so the writer fell back on its blanket slug_gap=0.018 for EVERY interior -- and the checker flagged our own fallback. The Rev2 row's slug_tab width + Small/Large limits (ingested 2026-08-31, never wired) now ride in tooling['slug'] and size every slug on every posting surface; both check surfaces judge with the same limits.
- •Colors & legend: the signal vocabulary explained, every color operator-definable record — Built 2026-09-04 (Jordan: 'explain the color scheme you're using in a legend... apply it to the settings; if we have a color blind operator, they may want to define their own colors'): ? Help -> Colours & legend -- one modal that IS both the legend and the editor: nine named signal roles (base layer / healed lines / healed arcs+toolpath / selection+tooling / clean-done / yellow / red / pending / secondary), each with its meaning in words, its glyph where one exists, and a colour picker. REFINED same day (Jordan): 'healed arcs & toolpath' split into two inputs (--toolpath its own variable); 'selection & tooling' DECOUPLED from the interface accent (--sel, canvas-only -- recoloring selection no longer restyles the app; a pre-split saved --accent pick migrates to --sel once); US spelling ('colors') on every user-visible string. Ten roles now. 10 live Playwright checks.
- •DXF mirrored entities (extrusion normal -Z): OCS flip applied at read; tilted normals refuse record — Built 2026-09-04 (Jordan's 44558B_7 catch -- 'a lot of the healing is offset the full width of the part to the left... why only for certain parts?'): when CAD mirrors an entity it may keep the entity's own coordinates and set the DXF extrusion normal (group codes 210/220/230) to (0,0,-1); the coordinates are then in that Object Coordinate System and map OCS (x,y) -> WCS (-x,y). The reader ignored the flag, so 33 mirrored ARCs + 4 mirrored CIRCLEs drew mirrored about x=0 -- a full part-width to the left -- while every LINE sat correctly (LINE and SPLINE store world coordinates by the format's own design; only OCS entities -- ARC, CIRCLE, LWPOLYLINE, POLYLINE -- carry the defect, which is exactly why the error hit only some of the part).
- •Heal/QC panel: Delete edit op, per-job heal cache (lens lag fix), our own lens names record — Built 2026-09-04 (Jordan's three asks on 44558B_7: mark-lens lag, a missing delete, 'you used verbatim "mark open polylines" which is MetaCam language. Recreate these with our own names'). (1) CACHE: every lens click re-parsed and re-healed the source from scratch (6.6 s on 44558B_7); server _job_prims now caches the pure heal per job, keyed on the source file's stat + job tolerance, returned as a copy; run_pipeline primes it, so the first click after an upload is already warm -- measured 6.63 s -> 0.01 s. A changed source, tolerance or re-upload misses the key and re-heals. (2) DELETE: heal_edit gains op 'delete' -- an armed click removes the whole contiguous chain nearest the point (an open stray, or a closed contour on the operator's explicit click), recorded and replayed like every edit, the per-row remove is the undo. Reach gate 0.25 in (the dims panel's picking reach): a replayed delete whose chain left the geometry SKIPS with the refusal's words, never deletes whatever is nearest now; deleting the last geometry refuses. The deleted prims ride in the edit report and the pipeline's fidelity measure excludes deliberately removed ink from the source side (the sheet-intake drops precedent) -- without that the delete read as a giant bogus FAIL and the unmatched points sent the grid ring-search across the whole part (a 15 s hang on a 6-line fixture, caught by the live verify). The gray base layer still draws the deleted ink forever. (3) NAMES: the five lens buttons and three edit buttons now carry our words -- Find open contours / Find duplicate lines / Find tiny segments / Find kinks / Find oversize arcs; Close gap / Make true arc / Drop tiny segments; engine lens keys unchanged. Proven: test_healedit 23->29 controls + 8 real-mouse Playwright checks on a scratch instance; introt 20 / sheet_intake 36 / rules 14 / sketch 21 / ocs 12 / dragcheck 15 green. Server+engine changes live after the next owner-turn restart; the renamed buttons serve from disk immediately -- a Delete click before the restart refuses honestly ('unknown edit op').
- •Heal/QC duplicates -- the lens, the one-click Drop fix, and the warn-at-the-door on operator adds record — Built 2026-09-07/08 (Jordan on B7787: assigned a toolpath, 'way too many cuts', clicked Find duplicate lines -- 64 found -- 'How do I fix it now?'; next day 'There are 5 features. Why 7 cuts?'). The lens existed with NO matching fix. heal_edit op 'drop_duplicates' -- ONE recorded edit, no point address: qc.find_duplicates is the SAME detector the lens draws (one detector per defect), so the button fixes exactly what the lens marked. THREE layers, each measured into existence on the real file: (1) PAIR DROPS -- per flagged pair the MORE-covered member is the victim (losing it loses the least unique ink; the detector's fracs ride on the flag now); ties pick the shorter, then the later; an already-dropped member resolves the pair, so chains of mutual copies keep exactly ONE span; victim choice is CHAIN-AWARE first -- a CLOSED chain keeps its member (a closed loop is a feature, the open chain overlapping it is the stray copy), between like chains the higher duplicated-fraction chain loses. (2) PARTIAL victims TRIM instead of dropping whole (_split_covered: coverage runs sampled on the victim against its kept twin at the detector's own DUP_TOL; sub-line/sub-arc on the victim's own geometry; runs under DUP_MIN_LEN absorb into their longer neighbour so boundary jitter never makes a sliver or pin-hole) -- unique ink is never removed. (3) RESIDUAL-OVERLAP SWEEP (_overlap_sweep): the pair gate is mutual >=55%, so a retraced curve segmented differently out vs back leaves sub-gate overlaps after the victims drop -- the sweep runs remaining prims pairwise (bbox prefilter) and trims the more-covered member's overlap to fixpoint; a welded/tangent joint's in-band run is always under the DUP_MIN_LEN floor, so real joints never trim (control 51: an out-leg of two segments + one-prim retrace back resolves to ONE copy, len exact). No duplicates = refusal in the engine's words; replay on clean geometry skips with the same words, never blocks. Removed ink rides the report as removed_prims -- and the fidelity exemption was REDESIGNED for it: exempt DEAD INK ONLY (near removed prims AND no healed counterpart in the band), because a dropped duplicate's KEPT twin still cuts -- the old source-side-only exclusion orphaned the twin's healed points and ring-searched minutes on B7787's 1.3M source samples; the new test keeps the twin measured and by construction can never orphan an output point either direction (the delete op rides the same test, strictly more honest). UI: 'Drop duplicate lines' one-click button in Manual edits beside the lens's own name. Proven: test_healedit 42->51 controls + the real B7787 through the server codepath on a scratch copy -- 64 dropped + the outer profile 1.32-in self-overlap trimmed (its dangling end closed itself), 194->131 prims, QC 139 flags -> 9 (all branch), heal fidelity PASS unchanged, every span cut ONCE -- the doubled lobes halve exactly (28.58->14.29, 53.22->26.61), review tools 11 contours 1 red/6 yellow. What remains on B7787 after the drop is the file's own truth: its strokes BRANCH (QC names them), so the operator resolves leftover construction strokes with Delete/Drop tiny segments -- aid the operator, never guess intent. Live after the next restart; the button serves from disk but rides the new engine. PROOF GATE 2026-09-10 (Jordan's RJB-DS catch on prod -- one flagged pair, his Drop click dropped a junction-wall arc whole, 2 dangling flags, the bridge deviated 27.98 thou; his 'Go' on the proposed fix): the drop must PROVE each removal harmless before it stands -- every removed span's samples must lie within the duplicate band (qc.DUP_TOL) of the ink that REMAINS (heal_edit._removal_dev, measured on the final candidate list so a victim whose cover was itself dropped cannot slip through; failures restore to fixpoint). The RJB failure was the absorption rule eating a diverging tip (0->~20 thou wedge, under the trim floor): a THIN JUNCTION WEDGE now refuses/skips in honest words ('thin junction wedge, not a retrace -- left untouched... use Delete to remove ink deliberately'), the _overlap_sweep proves every trim the same way before committing, and qc.find_duplicates carries the SAME wedge note on the flag itself (wedge field + 'Drop leaves it untouched') so the lens tells you before you click. Skip never force. Proven: test_healedit 51->57 -- the EXACT RJB pre-edit arcs as fixture (lens wedge note, strict refusal verbatim, replay skip, geometry untouched), RJB pair-2 sweep proof with independent sampling, and the full B7787 regression: all 64 real retraces still drop, output BYTE-IDENTICAL to the pre-gate code, worst removed-span deviation 0.00 thou, 0 wedge flags. Neighbour suites green (robust 50 / warns 17 / healedit_noop 8 / healadd 13 / sketch 21 / units_confirm 27 / sheet_intake 36 / dragcheck 15 / introt 24). REAL FILE WIRED 2026-09-10 (Jordan dropped the RJB-DS DXF; testdata/DWS-LH-2.5-LOGO-TREAD-RJB-DS.dxf is the committed fixture): first run on the true file caught a REAL GATE DEFECT the synthetic pair could not -- the proof sampled at 0.02 in and a 7.9-thou diverging bump slipped BETWEEN samples (near other junction ink the coarse samples all read covered), so the wedge dropped anyway; _removal_dev now samples at 0.002 in, well under the 6-thou band (a proof resolution, never a machine number). Proven on the real file end to end: heals 292 prims 0 open, exactly ONE dup flag = the wedge pair (49,50) wedge-noted on the lens; Drop leaves BOTH junction walls standing, the bridge survives, nothing dangles; the one harmless sweep trim proves at 0.56 thou under 0.001-in independent sampling; B7787 stays BYTE-IDENTICAL at the finer proof. test_healedit 57->60. WEDGE NOTES ON THE PANEL 2026-09-15 (Aristide 'Wedge note' -- the queue item offered to Jordan, built on the standing ratified order): the duplicate lens's flags now SHOW each pair's own note in the Heal/QC panel BEFORE the Drop click, client-only over the fields the engine already writes (note + wedge ride GET /healmarks verbatim -- no server or engine change). A wedge-noted pair renders FLAT in warn color, the engine's words verbatim ('thin junction wedge, not a retrace (diverges up to N thou); Drop leaves it untouched') -- unmissable where the operator is about to act; plain retraces fold under one chevron ('N retraces -- Drop duplicate lines removes the redundant copy of each', the grouped-findings pattern, so B7787's 64 never flood the panel); every row navigates -- click frames the pair's spot on the canvas, pulsed (the warnnav gesture); the canvas ring on a wedge flag reads warn instead of the retrace red, matching its note. Proven: app/verify_wedgenote.js 10 NEW live real-mouse Playwright checks over the public URL (both flag kinds on one scratch upload -- the RJB wedge arcs + a doubled line; note verbatim before any click, folded retrace opens to its own words, wedge row zooms 58.5->441.3, Drop takes the retrace and the wedge note + both walls STAND, zero page errors; fixture removed); verify_bandselgeo 13 + verify_snapdrag 10 re-run green. Client-only (app.js/app.css) -- serves from disk, live on refresh, no restart. WARN AT THE DOOR 2026-09-19 (Jordan's '3. I say go'; his doubled triangle edge -- 'I did not add any line twice, but I did click Add': the typed fields mirrored the drawn line and Add committed the span twice): an operator ADD that lands on existing geometry now warns the moment it commits. run_healedits runs qc.find_duplicates over the post-edit geometry (the SAME lens detector -- one detector per defect, so the door warning and the downstream net can never disagree) and names every add row in a flagged pair; the warning speaks on the row itself in the panel (yellow, with the row's own remove control as the one-click resolution) and in the commit toast ('the span would cut twice'). Warn never block: the save stands, the operator keeps or removes. dup_warns persists in healedits.json and recomputes on every save -- a cleared defect never keeps warning, a live one never stops. Customer dirty-CAD duplicates keep the after-the-fact road (lens + Drop duplicate lines) -- same defect class, different moment of detection. Proven: engine controls (add-on-edge warns, twin adds warn both rows, a clear-air add never warns) + verify_drawline 21 -> 24 (twin rows + a drawn-on-edge line warn at the door, removing the twin clears its warning while the live one stands) + healadd 12, gc56 25, healqc 14, setsize 13 green.
- •Upload door reads more than DXF: flat STEP exact, .ai/PDF at a stated width, DWG via LibreDWG conversion, honest refusals for the rest record — Built 2026-09-08 (Jordan: 'I need to be able to upload files other than DXF to the interface'). The app's one upload door now switches on extension; everything rides the SAME run_pipeline after conversion, nothing downstream knows the source format. FLAT STEP (.stp/.step): step_flat.py -- the proven build-151 path factored into an engine module. STEP carries true curves, so the top Z-normal planar face's outer+hole loops convert EXACTLY (full circle = the CIRCLE diet's two semicircle arcs), nothing healed away; units are the FILE'S OWN statement (CONVERSION_BASED_UNIT 'inch' = native inch untouched, else SI mm /25.4 in doubles), recorded and PINNED -- run_pipeline never re-infers a converted file's units from the written R12's magnitude (the sketch lesson generalized: job.units_pinned; the units-confirm modal stays off, the flip door refuses in honest words -- there is no unit question). A BENT part (cylindrical wall whose axis is off the sheet normal -- a bend, never a hole barrel) is a BLOCKED intake in the engine's words naming the K-factor (a materials-table value, ruled 2026-08-31) -- unfold is not wired into the door yet; the file is preserved. Broken topology / non-LINE-CIRCLE flat edges refuse rather than guess. VECTOR ART (.ai/PDF): the sheet-intake pattern -- the upload is PRESERVED as vector.<ext>, intake FLAGS with the stated-width door (an .ai/PDF carries no real-world dimension; the scale is NEVER a guess, PINLIST 14b's gate now an interface door). POST /api/jobs/<id>/vecextract {width} runs the proven vec_pipeline.read (true Beziers re-fitted to real arcs, specks dropped, fills/live-text counted) -> MetaCam-safe source.dxf -> run_pipeline; units pinned to the operator's statement; the conversion report (paths, Beziers->arcs+lines, specks, worst kink) renders on the Intake drawer with honest YELLOWs for live text (outline it and re-upload) and fills (strokes only, per the customer guidelines); re-convert at a new width always re-runs from the preserved file, the standing stale rule dropping downstream judgments with numbers kept. Refusals plain-400 in the engine's words, nothing changed. REFUSED AT THE DOOR (plain 400, nothing saved, honest words): DWG (binary, names the export-DXF ask), SVG (no reader yet), raster photos/scans (the 3A76 draft-only ruling -- ask for native CAD first), unknown extensions. Proven: test_upload_formats.py 17 NEW controls (build-151 numbers reproduced through the door: 37.15625 x 3.0 x 0.0625, 6 holes; bent BNM78651 blocked verbatim; EAGLES.ai at width 12 -> part measures 12.0000, re-convert at 24 re-runs; DXF path regression) + app/verify_upload_formats.js 8 live real-mouse Playwright checks on a scratch instance; neighbour suites green (units_confirm 27 / sketch 21 / sheet_intake 36 / healadd 13 / healedit_noop 8 / introt 24). Honest limits: STEP flats only (bent-part unfold delivery stays the board-script path until the K-factor column lands); .ai/PDF wants the width of the WHOLE artwork (one number, the vec_pipeline contract); SVG stays the recorded next reader; a drawing-sheet PDF print (pdf-dims-govern) is NOT this door -- vector art converts as drawn geometry, callout-driven rebuild stays its own goal.
DWG INTAKE 2026-09-19 (Jordan: 'are DWG files going to work?' before his 'Go' on the upload slate): .dwg joins the door. The server converts through LibreDWG's dwg2dxf reader (binary resolved from PATH, ~/.local/bin, /usr/local/bin, then app/tools; temp-dir conversion, 120s timeout) and the converted source.dxf rides the SAME pipeline as any DXF -- nothing downstream knows the source format, exactly the door's standing rule. The customer's original is saved as source.dwg beside the converted file and offered on the Geometry tab's Intake section (the job-file GET route serves it as octet-stream); jrec.dwg_convert records the conversion and the Intake drawer states it in plain words. A DWG that will not convert refuses in the converter's own words, including the honest 'no converter installed' when the binary is absent. PROD CAVEAT, flagged to Aristide for Sem: the converter is installed only on the dev box today (aarch64 build, deps libc/libm only); prod refuses DWG with the honest words until LibreDWG lands there -- decided NOT to vendor the 43MB binary into git. Proof fixtures: incoming ex_7.dwg (clean 2-part kit -> converts, splits, 2-row list on the one road) and the 3A76 DWG (dimensioned drawing -> converts, sheet-detected, direct door BY DESIGN). Trust boundary: LibreDWG's WRITER (dxf2dwg) emits broken files -- never fabricate DWG fixtures with it; only the reader is trusted. NEW app/verify_dwgtray.js 20/20 + multiupload 12, upload_formats 10, oneroad 17, srcdl 13, tray 18, plpick 17, pltabs 19 green.
- •Units confirmation on first open: autodetect proposes, the operator confirms; the BIG RED warning only on the extremes; 2026-09-20 -- per-card in/mm toggle on the part list (kit flips as a set), interrupting modal narrowed to the extremes, one-suspects modal at the tray door record — Built 2026-09-04 (Jordan, from the 44558B_7 lesson: 'we should ask the first person who opens the file to confirm the units ... a BIG RED warning ... only the extremes ... the extreme can go both ways'). ENGINE: dxf_io.to_inches(force='in'/'mm') -- the operator's statement outranks the header AND the magnitude inference (a header can lie: 44558B_7 declares $INSUNITS=1 on a metric part); note says 'operator stated'. SERVER: the intake record carries units_confirm -- what ran (in/mm), its source (header/inferred/operator), the part size under BOTH readings, confirmed flag, and the extreme red: fired ONLY when the reading that ran is wildly implausible AND the opposite reading is a plausible part (0.25 in to 2x the bed) -- both directions, exactly Jordan's two cases; an operator's statement clears it (their call stands). THE LARGE THRESHOLD IS THE MACHINE TABLE ITSELF (Jordan, 2026-09-04 refinement: 'Make the large threshold the table size, i.e.: greater than 120 inches with the GX 10K') -- read off tables.bed() at judgment time, never a literal; the message names the table ('bigger than the 120" machine table'); bed rides on /api/materials so the client's pre-block display mirror reads the same value. THE SMALL EXTREME IS PER-FEATURE CONSISTENCY (Jordan, 2026-09-04, the 50-unit-circle example: a 50mm ring whose perimeter holes read ~0.010" is the giveaway the file was inches -- 'a human would understand as being absurd'; the part itself is a plausible 2", so the whole-part bbox never sees it): the red also fires when at least 3 closed interior features, and at least half of them, each measure under 0.030" in the reading that ran AND read sensible (over 0.030") the other way -- both-ways gated per feature, so a couple of dirt artifacts on a correctly-read file never trip it, and the message reads the example back ('6 features measure ~0.010" -- absurdly small to cut; read as inches ~0.25"'). The 0.030" feature bar and the 3-and-half consistency counts are my picks, invited to change; the whole-part 0.25" rule stays as the degenerate case. Feature sizes come off the healed contours (heal chains them contiguously; open strays are dirt and stay out); the client pre-block mirror stays bbox-only honestly -- the authoritative feature judgment arrives with the server record. POST /api/jobs/<id>/units {units}: confirming WHAT RAN is RECORD-ONLY (downstream stages stand untouched -- a re-run would drop stages over a no-op); flipping re-runs the pipeline at the true scale and the standing stale rule fires (downstream drops, O-numbers kept, deliveries marked). Refusals plain-400 in honest words (sketch = typed in inches by definition; part list = no geometry of its own; junk value). The heal cache keys on the statement -- a flip can never serve stale-scale prims (_job_prims re-heals with the force). UI: the modal auto-opens on FIRST OPEN of a job whose fresh intake record is unconfirmed (once per session; Later defers) -- Inches/MM toggle preselected from autodetection, one click throws it the other way, the size shown under the selected reading, the red banner on extremes; records healed before the block confirm via the Intake tab's 'confirm / change units...' door (no nagging of already-worked jobs), which shows the server's red on the tab too. SAME BUILD (Jordan's two interface asks): a PROCESSING INDICATOR (spinner overlay) on upload and on the units flip -- before this an upload showed nothing until the job appeared; and the tool palettes carry a slight white glow in dark mode / a slight drop shadow in light. Proven: test_units_confirm.py 25 controls (6b: 150-inch fires under the table-size band; 6c-6f: Jordan's ring example fires as mm / clean as inches, genuine mm part clean, two artifacts stay quiet) + app/verify_units.js 16 live Playwright checks on a scratch instance (fixtures cleaned); introt 20 / healedit 29 / sketch 21 / ocs 12 / sheet_intake 36 / dragcheck 15 / rules 14 green. Server+engine live after the next owner-turn restart; CSS/indicator serve from disk immediately (the modal needs the restart -- it rides the fresh intake record and the new endpoint). UNITS FORCE RIDES EVERY RE-PARSE (same day, later -- found processing Jordan's bypass request, 6 x 44558B_10 on .080" steel): NINE server sites (run_review, dragcheck, quote, single-part post, postnest, both deliver DXF writers, the three part-list referenced-job heals) re-parsed source.dxf with a bare to_inches(), dropping the recorded statement -- on the lying $INSUNITS=1 header every one of those stages ran 25.4x too big under a confirmed mm force (44558B_10's persisted review carried coordinates to 59.55 on a 2.34" part while nest.json, reading part_bbox, sat at true scale). Fix: one helper _ufv(job) (sketch = never forced) passed as force= at all nine sites; _job_prims reads it too. test_units_confirm 25->27 (controls 21/22: a lying-header job flipped to mm reviews at its true 3.94" scale). The job re-ran at correct scale via the server codepath offline: review Mild Steel,0.08, nest 6 = 1x6 in Y, O33339 macro post, part DXF census PASS. Live after the next owner-turn restart. RADIO ROWS + STATED CONFIRM 2026-09-11 (Aristide's screenshot catch: the two-button toggle lit the UNSELECTED side brighter than the selected one -- inverted emphasis -- and the size consequence sat in small print; his pick of the offered options, 'be prepared to roll it back'): client-only redesign of the modal's selector -- radio-style rows, each carrying its reading's size IN the label at full size ('Inches (autodetected) -- reads 400.00" x 90.00" -- bigger than the 120" table' flagged red / 'MM -- reads 15.75" x 3.54"'; over-table flag is a display derivation off S.matopts.bed, the server's extreme judgment stays the red banner), and the commit button states the decision ('Confirm: Inches' / 'Confirm: MM') so a misread selector can never confirm the wrong thing unnoticed. verify_units.js updated to the new contract, 18/18 green on a scratch instance. Client-only (index.html/app.css/app.js) -- serves from disk, live immediately; one commit for a clean rollback if Aristide dislikes the look. FLOATING ELEVATION 2026-09-11 (Aristide, same thread: dark-mode modals and palettes must 'come off the screen a little better to indicate floating'): one shared --float recipe (dark: hairline light rim + soft glow + deeper drop; light: shadow alone) on all ten floating surfaces incl. floating panels, menus, every modal card and the toast; docked palette glow strengthened. Client-only, live immediately; verify_units 18/18 green. MOVABLE MODALS 2026-09-11 (Aristide, same thread, on the elevation screenshot: 'can you make these modals movable? so I can look behind the modal?'): all four modal cards (units, shop setup, colors & legend, bug report) drag by any spot that isn't a control -- the heading shows the grab cursor; the card moves over its centered base position, clamped fully inside the viewport (the panels' never-lost rule), and every fresh open re-centers it (position is transient, nothing stored); a drag starting on a button/input/label never moves the card, so no control loses its click. No backdrop-click-close exists on these modals, so a drag can never dismiss one. FIXED ON THE WAY (caught by the verify's real mouse): the shop-setup and bug cards had no max-height -- on a short window the wizard card overflowed BOTH screen edges, heading and buttons off-screen (its h4 measured at y=-111 in an 800px viewport); wizcard/bugcard/unitscard now carry max-height 88vh + inner scroll (the colcard precedent). Proven: app/verify_modaldrag.js 7 NEW live real-mouse Playwright checks (drag moves, off-screen throw clamps, button-drag no-op, reopen re-centers, wizard clamped at the bottom edge) + verify_units.js 18/18 re-run. Client-only (app.js/app.css) -- serves from disk, live immediately. UNITS PIN RIDES EVERY RE-PARSE 2026-09-15 (Jordan's Nest_9-15-1 screenshot -- the 7497/7498 part-list nest stacked parts on top of each other at qty 1): the pipeline honoured units_pinned (converted/split source.dxf written in inches by construction) but _ufv -- the one helper every re-parse site passes as to_inches(force=) -- only carried the OPERATOR'S force, so every re-parse re-ran the magnitude inference per site. 7497's split children carry giant-radius arcs whose full-circle padding inflated the written R12 header extents ~25x (the view-extents defect class, in the WRITER this time), the inference read those children as mm, and the part-list pack placed 25.4x-shrunk outlines at sliver pitches -- the pile-up on his sheet. Fix, both halves in one commit: _ufv returns force 'in' for any units-pinned job (one seam -- all thirteen to_inches sites fixed at once; run_pipeline's pinned branch keys the heal cache the same way), and emit.write_dxf_metacam's $EXTMIN/$EXTMAX now run emit._arc_extremes (the one arc-extents arithmetic) instead of center+/-radius, so a written file's own header tells the truth. Proven: test_split_door 13->17 (child pinned, _ufv carries it, sketch/force regressions) + test_view_extents 8->9 (giant flat arc round-trips through the writer and reads inch) + units_confirm 27 / sheet_intake 36 / upload_formats 17 / part_list 28 / pl_deliver 27 / plhand 25 / pl_post 26 green. Jordan's list re-packed through the live server's own door: 14 parts over 2 sheets, zero overlaps, audit +0.005 over ruled. Restarted same turn -- LIVE.
PER-CARD IN/MM TOGGLE + EXTREMES-ONLY DOOR CHECK (2026-09-20, Jordan: 'wouldn't an in/mm toggle in the Part List next to rotation/etc be a logical idea? That way the operator can clearly see something is wrong by the overall X/Y dimension, and have a quick toggle?' -- his 'Go' on the three-part proposal): (1) every part-list preview card carries an in|mm toggle beside qty/rotate/bridge/filler showing the CURRENT reading; clicking the other flips through the same recorded /units door -- re-heal at the stated scale, plans drop by the standing stale rule, the packed nest updates itself on the next Nest/Post landing (the auto-update machinery), one heavy undo step. UNITS BELONG TO THE FILE, not the row: a kit-born row (split child) shows and flips its parent FILE's reading and the whole set moves together -- half a kit can never be metric; the card tooltip says so. (2) SERVER KIT CASCADE: run_units on a split child ROUTES to its parent file; a flip on a split parent rescales every live child IN PLACE via _kit_cascade -- the child keeps its job id and every list reference, its units_pinned gains the corrective scale (ratio of the parent's new scale to old; returns to exactly 1.0 on a flip-back), dxf_io.to_inches grew a NUMERIC force so the pin's scale rides ALL the _ufv re-parse sites identically, and the child re-heals through its own pipeline (a refusing child lands blocked and named, never silent). (3) EXTREMES-ONLY INTERRUPTION: the first-open modal now fires only when the server's own extreme judgment speaks (bigger than the machine table / absurdly small) -- routine files keep the question one click away on the Intake section and the card toggle (the LIST is the review surface; supersedes ask-every-file at the door on Jordan's word). The tray drop gets the same rule: after the batch, ONE modal (uxmodal) lists just the suspect files with the big red words and an in/mm pick each; Confirm posts through the same /units door (a flipped kit cascades server-side, split already done or not), Later leaves the question on the cards. Proven live on dev: two-part mm kit uploaded, split, flipped in<->mm through a CHILD's card toggle -- both children rescale 3.94in <-> 100.00in, pin scale 25.4 then exactly 1.0 back, parent record follows; tray drop of an extreme file ($INSUNITS=1, 300in) + a routine file -- only the extreme listed, MM pick re-healed it, the routine file sailed through unconfirmed-and-unnagged. verify_pltabs 24 + partnav 15 green same turn. Suites that WAIT for the routine-file modal (oneroad, units, dwgtray, multiupload lineage) are now design-stale -- QUEUED for rework, stated.
- •Inches-only display; splines ride the gray base layer; material dropdown = EVERY Rev2 row grouped by type record — Built 2026-09-04, Jordan's three screenshots on 44558B_10. (1) INCHES ONLY (Jordan: 'don't display results in feet, keep all the results in inches'): the units modal / extreme-red sizes read inches at any magnitude -- server _size_words and the client formatter both; test_units_confirm control 4 now asserts inches, verify_units.js updated. (2) SPLINES ON THE BASE LAYER (Jordan: 'the background original cad layer isn't displaying splines. Am I correct?' -- he was): SPLINE/ELLIPSE entities are never folded into the polyline model, and _seg_json_source only walked polylines, so the 44558B_10 slot end-caps drew on the healed overlay but not the gray original. The base layer now samples splines+ellipses off the analytic curve (~0.02" display spacing; fidelity keeps its own 0.001 path), and _rot90_parsed rotates spline ctrl points + ellipse centre/major vector -- closing a LATENT frame bug: the shallow dict copy shared the UNROTATED lists, so a quarter-turned spline file would have measured fidelity against the wrong frame. test_introt 20->24 (spline cap on the base layer, rotated frame, heal PASS both ways). (3) MATERIALS FROM OUR TABLE (Jordan: 'the drop down is missing most of the material from our tables ... present me in this dropdown based on material type first, then the values that apply to that material type in a sub-menu'): material_options() offers EVERY Rev2 row -- 71 rows, 6 groups -- labels from the table (the '(MSO7)' MetaCam suffix dropped); one shared client builder renders native optgroups (type first, rows under it) in all three material selects (Part review, Quote, Part list) -- works identically on the three lenses. The recorded Mild Steel<->MSO7 post pairing is kept where the materials.json row exists; ANY other row (Stainless, Aluminum, Brass, Copper, Galv, unpaired Mild Steel gauges) carries its own Rev2 key as the post key, so a stage needing materials.json values refuses IN THE TABLE'S WORDS naming the row ('a new material is a row someone types') instead of the row vanishing -- Jordan feeds the values for Steel, Stainless, etc. and rows come alive as they land. test_matoptions.py NEW 10 controls + verify_matmenu.js NEW 9 live Playwright checks on a scratch instance (grouped optgroups, honest Stainless refusal verbatim, MSO7-paired row still plans, base-layer spline ink); verify_units 16 green with the inches wording. Suites green: introt 24 / units_confirm 25 / matoptions 10 / sheet_intake 36 / healedit 29 / sketch 21 / ocs 12 / dragcheck 15 / rules 14 / tooling_doc 37. Server-side: live after the next owner-turn restart; the client optgroup builder + inches formatter serve from disk now (pre-restart payload degrades gracefully to the old 8 rows, grouped).
- •Full table lead on clear-air ties + honest lead source; part tabs from the Rev2 part_tab column; app declares all boundaries cut-critical (interiors G41) with a consumable per-feature comp override record — Built 2026-09-04 on Jordan's three 44558B_10 questions ('Go'). (1) SLOT LEAD LENGTH: emit._pick_lead judged clear air on raw floats, so in a narrow slot every pierce tied at the half-width and sub-thou sampling scatter picked the lead length (0.084 ran on a 0.15 table value beside Jordan's dragged right-cap start). The chooser now judges the clear-air TIER rounded to a thou (the lead-aim-consistency standard) and prefers the LONGER lead inside a tier -- his dragged cap start runs the full 0.15 down the slot axis; a genuine clearance difference still outranks length. The pierce-standoff yellow stays -- slot half-width physics, not the lead. (2) LEAD SOURCE HONESTY: the record said table:lead_len while 0.082 ran; now table:lead_len only when the full table length runs, else rule:shortened-to-fit -- the drawer stops claiming the table. (3) PART TABS FROM THE TABLE: the Rev2 part_tab column (0.030 on Mild Steel, in the sheet since the 2026-08-24 ingest -- the brick-19 'awaiting width table' record was STALE) now rides tables.rev2_tooling (blank raises in the table's words); the app passes it at every emit/judge site via ONE helper (_post_defaults), so outers post held with tab ran+source table:part_tab; the per-feature tab_width override (0 = suppress) wins as before. Jordan's question IS the 2026-08-12 go the record was waiting on. cl_post excluded -- band perimeter tabs stay open with Jordan. (4) COMP: the engine's cut_critical default ('outer' = interiors are art) dated from the art-file era and the app never set it, so slots posted G40 and cut ~2x offset undersize; the shop's own programs comp every boundary (33215.NC 44/44 G41, 33199.NC 287/287). The app now declares CUT_CRITICAL='all' at the same one helper, and 'comp' joined tooling_doc.CONSUMABLE in the same commit as the engine input: per-feature overrides {'near','comp':'on'|'off'} -- the EDIT_TOOLING walkthrough's per-feature comp-off, source operator, round-trips, junk value refuses in the engine's words; UI comp row in the review drawer (force off/on, clear). Legacy paths byte-identical: tooling=None, cut_critical default, part_tab=None all unchanged (control proves it). Proven: test_lead_tab_comp.py NEW 19 controls + scratch-instance run of the real job (slots G41, outer 0.030 tab, O33339 kept, full-length lead from the dragged start); suites green: drag 23 / tooling_doc 37 / rules 14 / nc_warn 12 / dragcheck 15 / units_confirm 27 / sketch 21 / healedit 29 / nestseq 12 / part_list 28 / pl_post 26 / pl_tool 18 / pl_seq 27 / cl_post 18 / traverse 10 / common_line 24. The real job re-ran offline with Jordan's dragged starts riding; delivered O33339.nc refreshed (all boundaries comped, tab riding, same standoff yellows). Live in the app after the next owner-turn restart -- a comp/tab click before the restart is consumed only after it.
- •G-code header notes from the Rev2 table: used conditions named, used-only D registers in numeric order, M100 default + blocked variants -- 'This is a MetaCam feature, and as such, we can do better' record — Built 2026-09-04 on Jordan's three header notes (screenshot GCode_Notes_9-4-1.png, O33338's header) while awaiting Aristide's restart. (1) CONDITION NOTES: MetaCam marks the one condition a program uses with a bare '()' (the #107=107() corpus artifact); every condition number the program's boundaries actually USE now posts with its class name -- #108=108(PROFILE), #107=107(INSIDE), #106=106(SMALL) -- while the UNUSED lines stand untouched (#102=102 etc.), Jordan's ruling: 'so that the laser operator can still manually change one feature in a subprogram if they choose.' (2) D OFFSETS: only the registers the program uses post, in numeric D order, each noting its class -- #2001=.004(D1 - INSIDE) / #2003=.004(D3 - PROFILE); a register shared by two classes at different offsets refuses (one register holds one value on the control). (3) M100 FROM THE TABLE (Jordan: 'We will use the Rev2 table going forward. I just named it Rev2 to keep it straight on my end'): tables.rev2_tooling now carries the row's m100_default + variant columns (ingested since 2026-08-24, notes and all); the default posts noted (DEFAULT), the variants follow in sheet order blocked with a leading '(' so the control skips them, the sheet's own note riding inside the comment -- (M100[CRS1018,AI,ZHL,S25,GX,0.0800] High Pressure Air); the note's own parens are stripped so no comment nests parens (control-format hazard). The stale MetaCam-era key (M100[MSO7,0.080]) no longer posts as the machine word; it stays the internal lookup key and the (MATERIAL) line. Engine: emit._cond_lines/_dreg_lines/_m100_lines shared by the single-part AND nest main-program headers; shop dialect's OFFSET comments used-only in register order the same way. Legacy posts (tooling=None) stay byte-identical: #107=107(), single zero-padded (D07), M100[material]; a tooling dict without m100 (old callers/tests) also keeps M100[material]. The audit reads the annotated file clean (comment lines skipped by construction; control proves no structure anomaly). Proven: test_header_notes.py NEW 22 controls + 12 neighbour suites green (lead_tab_comp 19 / tooling_doc 37 / rules 14 / nc_warn 12 / drag 23 / dragcheck 15 / cl_post / pl_post 26 / traverse 10 / sketch 21 / units_confirm 27 / customer 26). The real 44558B_10 job re-posted via the server codepath offline: O33339 refreshed in viewer/44558B_10/ with the new header, same 9 yellows riding, number kept. Live after the next owner-turn restart.
- •Beam-offset toolpath display + per-feature condition class / custom D register overrides + rubber-band multi-select -- 'see the G41 doing its job', the tooling-pin D4 move record — Built 2026-09-04 on Jordan's two asks + multi-select ('Does this make sense?' -> both answered, built same turn). (1) OFFSET DISPLAY: 'show the toolpath, already offset from the table values, this way we can see the G41 doing it's job' -- emit._offset_path computes the beam-CENTRE path per boundary (line shifts parallel; arc keeps its centre, r -/+ d by travel direction x comp side; corner gaps joined with an arc about the shared source vertex, the control's own comp-B move), at the value the boundary's D register actually carries (class offset, operator d_offset, or legacy kerf); rides plan rows as offset/offset_val, review payload passes it through, and a fourth canvas layer 'Beam-offset path (comp)' (toolbar key B, ON by default) draws it fine-dashed under the nominal as-cut path. DISPLAY ONLY -- the program still posts nominal coordinates and the CONTROL applies the offset; nothing downstream consumes it; comp-off boundaries honestly carry none. (2) CUSTOM D REGISTER (the tooling-pin scenario: holes fitted to pins, operator dials the register at the control): per-feature override d_register (1..99) + optional d_offset (typed start value, 'maybe they want to start at .002'; blank seeds the class value) + optional d_label -- the header posts Jordan's exact form '#2004=.002(PIN CHECK)' (label uppercased, parens stripped -- no nested parens), the boundary posts D4, and the used-only rule follows: a class register nothing uses any more drops out of the header. The one-register-one-value guard covers operator registers too (forcing onto a class register at a different value refuses in the table-contradiction words). (3) CONDITION CLASS override, INDEPENDENT ('independent of the other selected options/overrides'): per-feature condition= profile/inside/small moves ONLY the #501 word to that class's condition number; the register stays its own knob; the used-condition header notes follow it. Both refuse without a Rev2 tooling row, in the engine's words. All four fields joined tooling_doc.CONSUMABLE in the same commit as the engine input (model rule); overrides_from round-trips them byte-identically; _feats_from passes them through review/dragcheck/quote/post/postnest; pl_post.assemble + the postnest part-by-part replication now carry comp/condition/d_* per copy (comp was a recorded replication gap -- closed). THE AUDIT KNOWS THE OPERATOR'S WORDS: nc_audit.audit(overrides=) binds each override to the boundary nearest its near point (bbox-min frame alignment, the geometry check's own rule) and judges it against the operator words -- no false 'table says' anomaly on a recorded choice; congruent twins (bbox within 2 thou) accept either word set, the honest form for expanded macro programs where one part-frame near stands for every arrayed copy; the same file WITHOUT overrides still flags, proven both ways. nc_warn.audit_and_inject passes overrides at every posting surface. (4) MULTI-SELECT: shift-click toggles features in/out, shift-drag rubber-bands (centroid-in-rect), the panel says 'N features selected -- edits apply to ALL selected', and every override edit fans through the one ovApply channel to each selected feature bound to its own centroid (sequence stays per feature; plain click collapses). Explicit selection outranks the identical-twin ALL fan. Proven: test_dcond.py NEW 17 controls (header form, class seed, used-only, independence, collision guard, refusal ladder, round trip byte-identical, audit both ways, offset at r-d on a CCW hole under G41 / outside on the outer, comp-off none, legacy byte-identical, pl_post replication) + app/verify_dcond.js 16 live Playwright checks on a scratch instance (real-mouse shift-click/band, D4 typed end to end, posted NC carries #2004=.002(PIN CHECK), audit clean); 14 neighbour suites green. Honest limits: server side live after the next owner-turn restart (a D/condition set in the app before it is consumed only after, the standing pre-restart note); offset corners are display-sampled (the audit still judges the nominal file); on a MACRO-mode nested post the audit's override sanction is congruence-scoped, not per-copy-bound.
- •Lead chooser: band-vs-best clear air + full-length angle scan -- the table lead runs wherever it fits, sub-thou scatter never buys it away record — Built 2026-09-04 on Jordan's slot-2 catch on O33338 ('Why is slot 2 not using the table's lead-in value?' -> 'I'll take your word for it. Build it and I'll test it.'). His dragged start sat 1.2 thou off the cap pole and the lead ran 0.093 on the 0.15 table value. TWO defects, both in emit._pick_lead: (1) the morning tier comparison (round clear air to a thou, longer lead wins inside a tier) still split candidates whose clear air STRADDLED a rounding boundary -- 0.0776 vs 0.0772 read as different tiers, so a 0.0004in clear-air 'gain' bought away 0.057in of lead; replaced by a BAND AGAINST THE BEST reachable clear air (CLEAR_BAND 0.001): every candidate within the band competes on length, a longer lead displaces only when longer by LEN_GAIN 0.005 (keeps a hole's pierce at its exact centre -- proven), a genuine clearance difference over the band still outranks length, insertion order keeps the perpendicular-first tie. No boundary to straddle. (2) the aim that runs the WHOLE table lead down a slot's axis lies between the 10-deg fan angles once the start is a few thou off the pole, and the local refinement cannot walk angle+length that far together -- the full length was never sampled; a FULL-LENGTH ANGLE SCAN (1-deg over the fan range, length pinned to the table value, best candidate's angle polished to ~0.01 deg) puts it in the running, the band deciding whether it wins. Both values construction margins of the search, never machine numbers; the standoff gate stays the table's and the slot yellows stay (half-width physics). Proven: test_lead_tab_comp 19->22 (off-pole drags +1.2/-2 thou run the full 0.15; hole pierce stays centred); drags 10-50 thou off the pole verified 0.15 offline; 44558B_10 re-judged via the server codepath -- all 3 boundaries 0.15 at his recorded starts; all 26 suites green. Live after the next owner-turn restart.
- •Post tab leads with the nested sheet program (Jordan's shaping of the two-doors confusion) record — Born Jordan 2026-09-04 after grabbing O33338 (single-part) when he wanted the nested-sheet form: 'there are 2 ways to post... a little confusing for my normal workflow.' SHAPED + BUILT 2026-09-07 (Jordan: 'post nested sheet should be option #1. The operator just spent all of his time optimizing the material usage and nest sequencing... individual part GCode as a secondary option.'): the Post tab drawer renders the Nested sheet program section FIRST, the single-part post below it under its own 'Single part program' header. Presentation only -- both doors stand, records/refusals/numbers per stage unchanged. Client-only (app.js), serves from disk, verified live on the 44558B_10 job same turn.
- •True-arc toolpath display; offset path is the main line, lead attaches to it; toolpath overlay on the Nest canvas record — BUILT 2026-09-05 on Jordan's asks 2+4 ('Can we fix the faceted display of the nominal so it represents your true arcs?' / 'we really should be able to see the toolpath' on the Nest tab; his 'Agreed' + 'Go'). ENGINE: plan rows (emit plan_samples) now carry `segs` -- the AS-CUT path as line/arc records (emit._seg_dicts, the /geometry seg shape; real centers+radii, tab gap survives) beside the sampled poly, which stays for old readers and the point checks -- and `offset_segs` (emit._offset_segs, the true-arc form of the beam-centre path: line parallel-shift, arc r+/-d, corner-join arcs about the source vertex at radius d) plus `lead_end_offset` = the offset path's start: comp engages ON the lead-in, so the display lead ends on the OFFSET path, never the nominal (Jordan: nominal attach 'is not representative of real life'). Display only -- posted coordinates stay nominal, the control applies the offset; NC bytes untouched. SERVER: run_review ships both seg sets + the attach point in the source frame (translation only, angles ride). UI: when comp is on the OFFSET path draws as the main severity-tinted line on Part review AND Cut plan (true-arc Path2D under the world transform -- arcs stay arcs at any zoom); the NOMINAL is its own layer, default OFF, key N, fine-dashed when shown (comp-off boundaries keep drawing nominal solid -- there is no offset path without comp); key B now also toggles the offset layer from the keyboard. NEST OVERLAY: every placement draws the reviewed plan's real paths + dashed leads + pierce dots by the SAME transform that places the geometry -- what the sheet program posts is what draws (parity with the part-list nest canvas). Pre-restart payloads degrade to the sampled poly. Proven: test_plan_display.py 12 NEW controls + app/verify_display.js 12 live checks on a scratch instance; 14 neighbour suites green. Live after the next owner-turn restart; the client serves from disk now and degrades gracefully. CUT-PLAN LEAD GAP CLOSED 2026-09-05 (Jordan: 'The Cut Plan tab is not displaying the lead-ins, why?' -- he was right: drawCutplan drew rapids + pierce dots but never the pierce->lead-end dashed segment, a brick-4-era display gap): the Cut plan canvas now draws each step's lead exactly like Part review (leadEndOf -- attaches to the offset path when comp is on). Web-only, live immediately, verified on his 44558B_10 cut plan.
- •Minimize warning nagging over minor issues -- acknowledge control proposed, not decided question — Jordan 2026-09-05, asked how to approve/clear the review warnings: 'We'll need to contemplate ways to minimize nagging over minor issues.' Recorded verbatim -- no mechanism built. Today: warn never block, findings ride into the NC as callouts (gcode-warning-callouts), no dismissed/acknowledged state exists. Proposed on the table: a per-finding acknowledge (who/when recorded, row dims, banner counts acknowledged separately; NC callouts still ride per the ruling -- dismissed is never erased). Physics findings (e.g. slot half-width pierce standoff) re-fire on every re-plan, so any acknowledge is per-plan unless ruled otherwise. Awaiting Jordan/Aristide shaping the ask.
- •Sequence numbers ride the toolpath -- pierce-anchored badges, Jordan's option-1 pick, BUILT record — Jordan 2026-09-14, MetaCam screenshot (boxes 1-7 riding the dotted rapid chain): 'The sequence number for each feature display on the toolpath rather than in the center of the geometry... easier for the human operator.' His pick same day ('Let's try 1. and we can change if we don't like it'): BUILT, client-only (app.js, serves from disk -- refresh, no restart). One helper seqAnchorFor: every sequence badge anchors at its feature's START point (the pierce), nudged 16px into clear air along the lead's own away-from-metal direction, so the numbers chain along the dashed rapids exactly like the head's route; overlapping badges slide further out along the same direction (proven crowded at 8px/in zoom, min gap 27.8px); anchors recorded per frame so a CLICK on a badge still selects its feature (reviewHit checks badges before geometry -- selection now works even where the badge sits off the part); Manual Sort pick badges ride 24px above the same anchor; the Part-review haloed seq labels follow the same spot (one vocabulary across both Tooling scenes). A degenerate lead falls back to an up-left nudge. Nest-tab numbered boxes STAY at part centers -- they number whole PARTS, not features, per the offer he accepted. Proven: app/verify_seqbadge.js 11 live Playwright checks (anchor at pierce+16, direction = along the lead, labels left the centroid, badge click selects, crowded nudge, Manual Sort pick lands by badge click, zero page errors; scratch fixture removed) + nav209 visual spot-check. Reversible on his word -- the anchor is one function.
- •Full 2D CAD suite -- Jordan asked, Aristide: yes but MODULAR; heal-only market raised question — Jordan 2026-09-06, verbatim: 'Before the next build, I have a fundamental question for you both: do we want to provide a full 2D CAD suite? Smaller shops would benefit because they would need no other 2D software, which is usually overpriced and underpowered. If we do a good job on the sketch tools, I don't see why this would be out of our wheelhouse. We would need a lot more than what we have now, however.' ARISTIDE same day: 'I do not mind that. but in that case build it modular... We have the healer. that is the engine. whether it is CAD that comes in from an outside source, or CAD from our own module, things will have to be healed (corrected for human error)... So what Jordan is asking in my mind is another module, that seamlessly fits of course, but doesn't HAVE to be there if someone just wants the healing/gcode generator? Some people may want the healing, but NOT the Gcode generator. we might have to consider that market too.' So: both partners YES in direction; the CAD suite = a MODULE over the one engine, never a fork -- and the same cut runs the other way: a HEAL-ONLY tier (intake -> heal -> QC -> dims -> MetaCam-safe DXF out, no machine tables needed) is a packaging cut the architecture already supports, since heal/QC demand no material x machine row -- tooling does. The sketcher's existing seam is the model: it compiles to a real source.dxf and nothing downstream knows it exists. My gap list stands (mouse drawing + snapping, trim/extend/fillet/chamfer/offset, mirror/pattern/move/copy, outlined text, driving dims in sketch mode, and the constraint solver as the one hard piece; brick-10 no-solver ruling would need superseding). Still a QUESTION: no bricks commissioned yet -- module boundaries and the tier cut get shaped by both partners before anything builds; per-nest sequencing stays the queued next build.
- •Manual nest placement -- operator can place/override/do whatever they need; commissioned as the next brick recordmixed — Jordan 2026-09-05, ruling on custom nests (asked how to add columns that don't finish max Y): 'Yes, we need to include this ability. If we are able to build the best of both worlds: superior automated nesting AND enabling the operator to manually place/override/do whatever they want or need, then we will be lightyears ahead of anyone else. Software developers want a one-size-fits-all product that forces the operators to adhere to their way of doing things, and as such (myself included), we end up hanging onto old, sub-par programs that allow us to do everything manually, at the cost of labor-intensive workflows, because the end result is what we ACTUALLY need. What other developers don't take into account: the cleanliness of theory rarely matches the messiness of reality; we don't know what the operators are up against in reality, so we need to provide every option and tool possible.' The aid-the-operator philosophy at nest level: automation seeds the layout, the operator may hand-place. COMMISSIONED as the next brick (mixed: the ruling stands; the editor is intent until built): drag placements, add/remove copies (e.g. extra columns short of max Y), positions persisted ran+source, the pack's own audit re-judging the hand layout (gap/lead findings warn never block), posted programs riding the hand layout with numbers kept per the stale rule. Today's levers recorded honestly: qty opens columns Y-first, finish-even-columns rounds up, X/Y spread + gap increments. BUILT 2026-09-05 (Jordan's 'Go' on 'How do I add columns for a custom nest?'): the true-nest Nest tab. ENGINE nest_rules.judge_layout -- the hand layout judged on the part's sampled TRUE outlines (never bounding boxes): off-sheet / crossing outlines RED, under-gap / under-margin clearance YELLOW, measured values in the words; contour jumps in the sample never read as segments. SERVER POST /api/jobs/<id>/nestedit -- the client sends the WHOLE layout ([x,y,rot] per placement, rot 0/180 only per the two-orientation ruling; malformed = plain 400 nothing saved, the nestseq pattern); a good save persists ran+source:operator on nest.json (layout_edit: date, findings, notes), recounts qty_filled, drops the posted nest program (every sheet keeps its O-number) and clears a clicked nest sequence (it indexed the old placements -- named, never silent); stage flagged on findings, WARN NEVER BLOCK (an overlapping layout saves flagged and posts flagged). THE TWO-SHAPES RULING HOLDS: a hand layout still forming regular arrays (an added column, a moved block) posts MACRO unchanged; one that breaks the lattice falls to PART-BY-PART with the why recorded in mode_why. UI: 'Edit layout by hand...' on the Nest drawer -- drag placements with the mouse (move cursor, empty canvas still pans, drop rounds to 4 decimals, draft only -- nothing saved until Apply), Add copy / Rotate 180 / Remove on the selection, Apply judges+saves, Cancel/Escape drops the draft, Re-nest = back to automatic; findings render red/yellow in the drawer with the HAND-EDITED banner. Proven: test_nestedit.py 28 NEW controls (engine judge, refusal ladder verbatim, program dropped number kept, macro-vs- part-by-part both ways, overlap red, re-pack replaces) + app/verify_nestedit.js 11 live real-mouse Playwright checks on a scratch instance; nestseq/rules/tooling_doc/nc_warn/sketch/ drag/matrecord/units_confirm/healedit suites green. Honest limits: the editor moves WHOLE placements (per-feature overrides stay on Part review); no live judge while dragging -- Apply judges (the posted-NC sheet-wide audit stays the final gate); per-job sheet size is the commissioned next brick (pin sheet-size-input). Live after the next owner-turn restart. V2 BUILT same day (Jordan's three asks + 'I say build it and let me test it'): (1) SNAP-TO-SPACING on drag -- free move stays the default; a 'snap to spacing' toggle (X/Y values seeded from the ruled gap, typed-overridable) eases the dragged part against a neighbor at the set spacing or into line with its row/column once it comes close; Alt holds the move free. Snap measures rotated bboxes (a placement convenience); the judge on Apply still measures true outlines. (2) ANY-ANGLE ROTATION -- +90/180 buttons plus a typed free angle; [x,y,rot] turns about the part centre, ONE convention shared by the judge (nest_rules.rotated_outline -- off-sheet/pair measures on the rotated true outline), the placed geometry (server place()/wpt()) and the posted sub (emit._rot_lines: text-level rotation of the emitted body about the part centre, modal one-axis moves post both axes after the turn, I/J rotate as offsets, G2/G3 and comp side untouched; 0/180 keep the exact closed forms byte-identical). (3) ARRAY STAMP -- count X x count Y at typed pitches seeded from the selected orientation's bbox + snap spacing, stamps a grid from the selected part (MetaCam's Edit Array move, draft-only until Apply). SNAP MEASURES THE TOOLING ENVELOPE -- 2026-09-05 (Jordan: 'Snap offsets should go to the tooling boundaries (i.e.: lead-ins), not part geometry'; the nest-objective-function rule -- the unit is part + lead + pierce envelope, never bare geometry): the editor's snap-to-spacing and the array-stamp pitch seeds now measure the part bbox UNIONED with every planned pierce point and lead end from the reviewed plan (the very leads the sheet program posts), rotated with the placement's own orientation -- on the real 44558B_10 part the X envelope grows 2.3386 -> 2.4886 (the outer lead's pierce) so a 0.2 snap leaves lead room instead of the 0.050 clear-air yellow his hand layout posted with. Client-side only (falls back to the bare bbox when no plan is loaded, honestly); the judge on Apply still measures true outlines and the posted-NC audit stays the final gate. Serves from disk, live now. SAFETY SET BUILT 2026-09-05 (Jordan's three asks after his 46-pc hand layout, his 'Go'): (1) UNDO + WHOLE-NEST DRAG -- every draft change (drag, add, remove, rotate, stamp, spread) pushes a snapshot; Undo button + Ctrl+Z step back one change at a time (a grab that never moved pops its own snapshot); dragging moves the WHOLE nest by default as one rigid body -- his 'default click should select the entire nest' -- with 'single part' the explicit pick, so an accidental nudge is either harmless or one Undo away. (2) RE-NEST SAVES THE HAND LAYOUT (his 're-nest deletes all the changes made by the operator'): run_nest stows a replaced layout_edit's sheets as nest.hand_saved (carried across further re-packs, superseded only by a newer hand edit); the Re-nest button confirms first when a hand layout exists; POST /nestrestore puts it back through run_nestedit's own door -- re-judged on true outlines, ran+source:operator, posted programs drop with every O-number kept, repeatable. (3) SPREAD Y ON THE DRAFT (his screenshot ask 'can I copy the spacing of your ideal spread Y to my custom nest?'): nest_rules.spread_hand_layout -- a thin adapter over pl_spread.spread_y, ONE evening arithmetic with the part-list screen (even gaps top/bottom/between, required minimums max(gap, margin) never violated, partial columns capped at the fullest column's rhythm with leftover to the top as one clean rectangle -- exactly his 17+10+10 screenshot shape); extents measure the TOOLING ENVELOPE (outline + the reviewed plan's lead/pierce points, the 2026-09-03 lead-envelope lesson) with the pierce standoff joining every between-gap minimum; POST /nestspread is READ-ONLY (the dragcheck pattern) -- the spread lands on the DRAFT, Apply saves as ever, X never moves. Proven: test_handspread.py 22 NEW controls + app/verify_handsafe.js 11 real-mouse Playwright checks on a scratch instance; nestedit 38 / seqcorner 13 / progstyle 24 / nestseq 20 / pl_spread 32 / rules 14 / sketch 21 / nc_warn 23 / tooling_doc suites green. Undo, whole-nest drag and the re-nest confirm serve from disk now; Spread Y and Restore ride the new endpoints -- live after the next owner-turn restart. ENTITY GROUPS 2026-09-05 (Jordan, after his layout redo: 'I hit "add copy" ... and my copy moves along with the original nest next to it. Each nest should behave as its own entity so that we can move them around and change them independently'; his 'Go'): every placement in the draft belongs to a GROUP -- the packed layout opens as one, each Add copy starts its own, a Stamp is one block (base included) -- and the default drag moves the grabbed part's own group as one rigid body; 'single part' and 'the whole sheet' are the explicit picks. Groups ride the saved layout (layout_edit.groups, validated to mirror the placements -- mismatch is a plain 400, nothing saved), survive Apply/reload, the re-nest stow AND the restore; the judge and every posted program read the flat placements only -- a group never changes what cuts. Undo snapshots carry them. Proven: test_handspread 22->27 + app/verify_groups.js 12 real-mouse checks on a scratch instance. The grouped Apply and the reload round-trip ride the new server -- live after the next owner-turn restart (pre-restart the editor still groups in-session; a pre-restart Apply saves the layout but drops the groups honestly). ENTITY-DRAG SNAP FIX 2026-09-05 (Jordan's catch the next morning: 'the X-spacing doesn't appear to use the tooling boundary instead of part geometry. This matters.'): the entity-groups build had made the group drag ('its own nest', the new DEFAULT) skip snap entirely -- so an added copy never eased to the tooling boundary, the exact workflow the snap was built for; what he got was eyeballed part-geometry spacing. Fixed: a group drag snaps AS A WHOLE -- the moving nest's union TOOLING envelope (lead-ins + pierce included, each member rotated with its own orientation) eases against every stationary placement's tooling box at the set X/Y spacing, one rigid adjustment for the whole entity; in-line candidates align part GEOMETRY (tooling edges would misalign 180s by the lead); Alt keeps the move free; a whole-sheet drag has nothing stationary to snap to. Single-part snap unchanged. Proven: app/verify_groupsnap.js 9 NEW real-mouse checks on a scratch instance (the snapped gap holds the 0.15 lead room -- 'NOT bare geometry + spacing' asserted directly; Alt stays free; part-mode regression lands the same slot) + verify_groups 12 / verify_handsafe 11 re-run green. Client-only -- serves from disk, live now. BAND SELECT + DELETE + REGROUP 2026-09-05 (Jordan's ask after his restored pre-groups layout came back as ONE entity: 'What would be nice is if I could rubber-band select entities and delete them. Then it would be easier to start over with just one copy of a part'; his 'GO!'): shift-drag on the edit canvas rubber-bands PLACEMENTS by center-in-rect (the review multi-select gesture, one UI language; shift-click toggles one; a plain click or empty-canvas click collapses the selection); the selection row offers Delete (button or Delete/Backspace key -- strip to one copy in two gestures), Make one nest (the selected parts become ONE entity -- the regroup tool a restored pre-groups layout needs) and Split into parts (each selected becomes its own nest; with no band, a clicked part's whole nest splits into singles). All draft-only until Apply, every move snapshots first so Undo/Ctrl+Z steps it back; deleting everything is allowed in the draft with an honest toast (Apply refuses an empty layout in the server's own words). Selected parts carry the accent box; groups ride Apply exactly as before. Proven: app/verify_bandsel.js 13 NEW real-mouse checks on a scratch instance (band selects 3-of-6 center-in-rect, shift-click toggles out, grouped pair drags as one with outsiders untouched, split carves it back, band+Delete key to one copy, Ctrl+Z restores parts AND groups, empty-draft honesty, 5-part Apply round-trips groups) + verify_groups 12 / verify_groupsnap 9 / verify_handsafe 11 re-run green. Client-only -- serves from disk, live now.
- •Nest macros: one P9997 sub PER ORIENTATION at any angle; only a one-off buried among arrays forces part-by-part ruling — Jordan 2026-09-05, correcting the claim that a non-180 rotation drops the macros: 'when it comes to the GCode, a new orientation of the part, at any rotation, would become a new subprogram and will be able to be called out by a new P9997 if it is "nested" meaning repetitions of that orientation in X or Y. Mitsubishi lasers will let you use as many P9997 or P9996's in a row that you want, but will not like it if you have a "one-off" part buried in there (G52...), in which case we'd have to switch to part-by-part.' So macro eligibility is NOT 0/180-only: every orientation whose placements form regular arrays posts as its own P9997 sub (server groups placements by rotation, subs numbered main+42000+k in angle order; the 2026-08-31 two-orientation alternation ruling stands as the AUTO fill pattern's shape, this governs what the POST accepts). WIRED same day into run_postnest (any-angle groups through _lattice_arrays; irregular lattice still falls to part-by-part with the why) and proven on a 0+37.5+90 hand layout: three subs, audit clean on the expanded program vs placed geometry. K1Q1 ANSWERED (Jordan 2026-09-05, same message as the Main Program Style ruling): 'I like your idea that if we're in Nest Macro mode we should use P9997 with K1Q1 so it's a single part inside of a nest macro' -- a lone placement STAYS a 1x1 P9997 array in macro mode, the bare-G52 one-off is never emitted; the operator who wants G52-per-part switches the Main Program Style toggle (pin main-program-style).
- •Main Program Style toggle: Nest Macro (P9997, K1Q1 for a lone part) vs Part-by-Part (G52 + M98P sub call per part) record — Jordan 2026-09-05 (ruling, same message as his K1Q1 answer): 'Can we make a toggle for Main Program Style: Nest Macro vs Part-by-Part? I like your idea that if we're in Nest Macro mode we should use P9997 with K1Q1 so it's a single part inside of a nest macro. Switching to Part-by-Part mode would skip the macros and just give a G52 for each part.' BUILT same day. The toggle rides the nested-sheet post (POST /api/jobs/<id>/postnest {style: 'macro'|'partbypart'}; junk value = plain 400, nothing saved), persists on job.json so re-posts keep the choice, and is recorded on postnest.json as program_style {chosen, source, ran} -- chosen beside ran, because an override (Manual Sort / nest-level clicks) still FORCES part-by-part in macro style per the 2026-08-28 two-shapes ruling; the choice stands recorded and macro returns when the override clears (proven in controls). MACRO (default): the P9997 form unchanged, a lone placement = a 1x1 K1Q1 array. PART-BY-PART: emit's g52 nest style -- one G52X..Y.. per part shifting the work frame to the placement (Fanuc local-shift semantics, absolute replacement), the SAME part-local reviewed toolpath inline per copy rotated to its own orientation via _rot_lines (every per-feature override -- Manual Sort order, tab size, lead length, dragged start, comp/condition/D -- consumed ONCE on the one body, so a forced start lands at the same relative spot on every copy by construction), FEATURE numbers global in file order so audit callouts name boundaries the operator finds in the shipped file with no macro mapping, G52X0Y0 clearing the shift before M30. This REPLACES the old fully-expanded absolute part-by-part form (brick 8) on the true-nest post. AUDIT: nc_audit.expand_g52 expands the shifts to the absolute frame (I/J untouched -- relative offsets) and the same audit judges it against the placed geometry; a single-G52 program (single-part margin line) is never this form and keeps its path byte-identical. UI: 'main program style' select on the Post tab's nested-sheet section. Proven: test_progstyle.py 24 NEW controls (structure, rotated section on placed geometry, persistence, forced-override interplay, K1Q1 lone part); nestedit 38 / nc_warn 23 / nestseq 12 / tooling_doc / rules 14 / drag 23 / sketch 21 / dcond / pl_post 26 / lead_tab_comp 22 / header_notes 22 / matrecord 30 green. Part-list programs (pl_post) keep their own one-program absolute form -- a different surface, unchanged. SUB-CALL FORM 2026-09-06 (Jordan, ruling: 'G52 part-by-part should still use a main program with a sub-program callout' -- refining his 'skip the macros and just give a G52 for each part': skip the NEST macros, keep the subprogram form; BUILT same day): the part-by-part main is one G52X..Y.. + M98P<sub> per placement, subs carry the part-local reviewed toolpath ONCE per orientation (numbered main+42000+k in angle order, the same corpus convention as the macro subs; every per-feature override still consumed once on the one body), G52X0Y0 clears before M30 -- short program, one edit in a sub reaches every copy, the same editability the macro form has. nc_audit.expand_g52 splices the called subs shifted (an M98 call to a sub not in the file, e.g. the pierce macro, passes through), renumbers FEATUREs globally, and returns the call list so the audit folds findings per (sub, local bnd) and nc_warn's spot markers land INSIDE the sub -- the macro program's callout discipline, extended. The 2026-09-05 inline form (body per copy, global FEATUREs) is legacy: delivered programs stand and still expand/audit (calls=None). boundaries_program now records the one part's count, the macro form's own convention. Proven: test_progstyle 24->29 controls (M98 main form, sub-local FEATUREs, call mapping, spot marker inside the sub, legacy inline regression, subs on postnest.json) + nc_warn 23 / seqexact 16 / nestedit 38 / seqcorner 17 / nestseq 27 / tooling_doc / rules 14 / drag 23 / sketch 21 / pl_post 26 / dcond green. STICKINESS FIX 2026-09-06 (folded into the per-nest sequencing build): the Post tab's style select used to snap an un-posted pick back to the persisted value on any drawer re-render, and every post sent the select's value so an untouched default flipped the record to source:operator. Now the pick rides client state until the post consumes it or another job opens, and the style is sent ONLY when it differs from what the job carries -- an untouched default posts with the record staying rule. Proven in verify_gseq.js checks 8/9.
- •Typed condition number + note per feature -- #501=#102 with the operator's own header note record — BUILT 2026-09-05 on Jordan's ask ('If I wanted to make this feature use condition class #102, how would I do it? Could I put my own note like how we do with custom offsets?'; his 'Go'). The custom-D-register move on the condition side: per-feature overrides `cond_number` (raw #-var, 102..111 -- #112-#120 do not exist on the control and #121/#122 carry the part size, refusal names that) + optional `cond_label`; the boundary posts #501=#102 and the header's used-only rule notes the line with the typed label -- '#102=102(ETCH TEST)' -- or OPERATOR when none typed (label sanitized like every header note: uppercased, parens stripped). A vacated class condition goes back to its bare line (hand-editable, per the header-notes ruling); a number shared with a used class or a second operator note carries all names '/'-joined on the one line. EXCLUSIVE with the class knob per feature ('one condition knob per feature' refusal); the D register stays its own decision. Both fields joined tooling_doc.CONSUMABLE in the same commit (model rule); overrides_from round-trips byte-identically; _feats_from passes them through (review, drag judge, every post read them identically); pl_post.assemble + the postnest part-by-part replication + run_partnest carry them per copy; nc_audit binds the override and judges the posted words against the operator's number -- no false 'table says' anomaly, and the same file WITHOUT the override still flags. UI: 'custom condition' row in the review contour panel (number + note inputs, set/clear; a class pick clears a typed number and vice versa -- one condition word per feature, enforced in ovApply's channel). Proven: test_dcond 17->27 controls + app/verify_condnum.js 14 live Playwright checks on a scratch instance (his exact #102 + note end to end, refusals, round trip across a fresh page, re-post moves the header notes); 14 neighbour suites green. Live after the next owner-turn restart.
- •Macro-program warning callouts group per sub boundary with copy counts; CONDITION-CLASS / M100-SELECT header renames record — EXTENDED 2026-09-17 (Jordan's O33342 screenshot -- 219 near-identical header lines, 'Can we not list every single warning in the GCode? No human wants to see this'): the header now GROUPS identical finding classes -- nc_warn.inject strips the per-instance parts of each sanitized message (sub number, ALL-k-COPIES count, spot coordinates); findings that then read identically are ONE numbered header line with honest totals ('-- N INSTANCES ACROSS M SUBS'), a lone finding keeps its full words, and the top callout reads '(CONTAINS 219 WARNINGS -- 12 DISTINCT)' when the counts differ (the ruled real-total callout stands). Spot markers carry the GROUP number so header and in-body marks stay in step; the audit-transparency recheck is untouched (comments only). Proven: test_nc_warn 23 controls on the new format incl. the O33286 macro fixture; synthetic CAMP-shaped control collapses 42 findings to 6 lines. Applies to every posting surface through nc_warn.audit_and_inject. --- ORIGINAL RECORD: BUILT 2026-09-05 on Jordan's O33339 catch ('the warnings in the header of the main program do not refer to the real BND numbers in the posted GCode... you should have exactly 2 yellow warnings. Am I wrong?' -- he was right). The audit STILL judges the expanded part-by-part form the machine executes (ruling unchanged), but the header callouts on a MACRO program now speak the shipped file's own language: expand_p9997 records boundaries-per-sub, nc_audit maps every expanded bnd back to (sub, local bnd), rules.check_pierce_nest groups tight pierces per sub boundary -- one finding per (sub, local), WORST clear air across copies, honest copy count, no 8-worst cap or rollup needed ('PIERCE BND 1 OF SUB O75339 CLEAR AIR 0.0778 < 0.15 STANDOFF -- ALL 36 COPIES') -- and rules.merge_copies folds arrayed repeats of every other rule the same way (all expanded bnds kept so spot markers still cover the array; one marker per sub bnd now, not one per copy). Proven on his exact file: 9 lines naming phantom bnds 76..97 -> exactly 2 warnings, and the old header had actually hidden slot 2's own worst (0.0784, buried in the rollup -- all 8 named lines were slot 1 instances). Single-part programs unchanged. SAME BUILD, his two renames: (CONDITION-VARIABLES) -> (CONDITION-CLASS), (MATERIAL-SELECT) -> (M100-SELECT) in every dialect's header (no parser read either string; one test filter updated to match the M100[ variant form). Proven: test_nc_warn 12->17 controls (O33286: 10 findings = 5 holes x 2 subs covering all 950 instances) + 12 neighbour suites green. Live after the next owner-turn restart.
- •The plain-words MATERIAL RECORD replaces the (MATERIAL : MSO7,...) header line -- job-card traits in the code and on the floor pages record — Jordan 2026-09-05 ruling on O33339's '(MATERIAL : MSO7,0.080)': 'I think that particular line could be replaced. MSO7,0.080 serves no purpose. Our M100s offer the laser operator adequate options, but right now we rely on a paper job card to identify specific material traits, and this could enhance that by putting that information both in the code, and on the pdf with the nest and parts.' Field list confirmed same day: type, grade, thickness, customer-supplied vs shop-procured, plus lot/heat ('put a place to include lot/heat number, but it is not used by us often. Not saying it's never used.'). BUILT same day. ENGINE: emit.write_gcode_macrob(mat_record=) -- emit._mat_lines replaces the bare post-key line with job-card comment lines in that order ((MATERIAL/GRADE/THICKNESS/SUPPLY/HEAT/LOT : ...), values sanitized like every typed header note, blank fields never post a line); M100 untouched -- it stays the machine word; mat_record=None byte-identical (legacy callers, board scripts). cl_post passes it through (one kwarg, the shared writer). AUDIT COMPAT: nc_audit._header_material returns a key only when the line's value is key-form ('<name>,<thickness>'); a plain-words header yields None and the explicit material= every app surface passes governs -- legacy delivered programs still parse. SERVER: job.json material_record (typed fields + provenance, 'who typed it awaits operator accounts'); GET/POST /api/jobs/<id>/matrecord = annotation only, record-only (saving drops nothing -- the next post carries it; delivered programs stand per the no-redelivery precedent); junk field/supply/over-60-chars refuse plain-400 with nothing saved. ONE effective dict (server._matrec): typed fields over table seeds -- type + thickness seed from the assigned Rev2 row (seed never bind), sources recorded per field (operator vs table:material-row). Every app posting surface passes it: quote, post, postnest (macro + part-by-part), plpost, commonlinepost. PAPER: the same dict rides packet.json and renders on all three floor packets (single/nest/part-list) -- 'in the code, and on the pdf with the nest and parts'. UI: 'Material record (job card)' section on Part review AND the sales Quote drawer (one renderer, sales and operator share it). Proven: test_matrecord.py 30 controls + 16 neighbour suites green. Honest limits: part-list jobs carry seeds from the list's material but have no typed-entry surface yet (their two tabs); the board deliver scripts stay legacy-line (mat_record=None, byte-identical); customer-sheet material sentence unchanged (already plain words by derivation).
- •FEATURE replaces BND in posted programs -- Jordan's word, swept end to end, both markers parse record — Jordan 2026-09-05: 'Can we replace all the BNDs with something a bit more modern? It feels so 90s.' -> same day, 'Let's go with FEATURE' -- his own ruled vocabulary (2026-08-28: MetaCam's entities are called features going forward). BUILT same day: emit.write_gcode_macrob and cl_post mark every boundary (FEATURE:n); finding/anomaly messages say 'feature n' (rules.py comp/tab/pierce/outer-last wording, nc_audit tooling-mismatch/micro-block/geometry-deviation wording, cl_post's common-line findings, the app's cut-plan step rows and unconsumed-override lines). The parsing contract holds BOTH markers -- nc_audit (_load stop line, _comp_words, _tooling_words, _short_blocks, expand_p9997's per-sub counts + renumbered re-emit), nc_warn (_macro_map + spot-marker anchors), pierce_clear.parse_nc, cl_post's band splitter/timer/audit, compare_nc (dialect sniff, parse, header) all accept (BND:n) too, so every delivered program still audits byte-for-byte (proven: O33286's macro expansion + ex2's plain audit unchanged; a FEATURE post rewritten to BND markers judges identically). Internal dict keys (bnds, bnd ids) stay -- they are code, not shipped words. test_nc_warn 17->23 controls; 20 suites green. Live in the app after the next owner-turn restart.
- •Per-job sheet size: sales inputs sheet size(s) at quoting; operator changes it easily at any time while nesting record — Jordan 2026-09-05 ruling (his drop/reorder scenario): 'While quoting: sales team should input sheet size(s). While nesting: operator should be able to change sheet size easily at any time.' BUILT 2026-09-07 on his 'Go on all': POST/GET /api/jobs/<id>/sheetsize -- {w,h} inches (w along machine X, the operator's word never auto-swapped) or {clear} back to the machine bed; RECORD-ONLY save (the units-confirm precedent: nothing drops, an existing layout stands byte-identical and the drawer names it stale in a yellow until a re-nest -- the operator's work is never silently moved); junk refuses plain-400 in honest words nothing saved; an over-bed sheet saves WITH the yellow in words (warn never block -- the physical call stays the operator's). All three pack-time doors consume it via one helper server._sheet_wh: the TRUE nest (run_nest), the PART-LIST pack (run_partnest) and the COMMON-LINE plan (run_commonline); nest.json/partnest.json record sheet ran+source (operator vs machine bed); quote's auto-nest and every downstream reader (judge, spread, delivery, canvas) ride the persisted record unchanged. UI: one Sheet size renderer on three doors -- Nest drawer, part-list Nest drawer, Quote drawer (the ruled sales door). Proven: test_sheetsize.py 20 controls + app/verify_sheetsize.js 10 live real-mouse Playwright checks (typed 48x48 end to end, stale-layout yellow, over-bed, back-to-bed, sales lens). Live after the next restart; the drawer UI serves from disk but rides the new endpoint. Honest limits: ONE sheet size per job today (the ruling's 'size(s)' plural -- several stock sizes with the packer choosing -- is a later extension); who typed it awaits operator accounts.
- •Sub-program numbers TRACKED in the shop sequence + manual override -- never handed out twice, kept per job, typed on the operator's call record — EXTENDED 2026-09-18 (Jordan: 'In the Post tab, it's not letting me override the program number, why is this?'; Aristide 'go'): measured cause -- the field's own placeholder shows the number AS 'O33342', so operators naturally type the O, and the single-part and nested-sheet doors read the field with a bare parseInt -- 'O33344' parsed NaN and the post SILENTLY ran with the job's kept number, no message (the part-list door already stripped the O since the 2026-09-17 build; the asymmetry was the proof). Fixed client-only in app.js: ONE shared parser (onumField) on all three doors plus the per-orientation nest sub fields -- strip a leading O/o and whitespace, then digits only; text that still does not parse REFUSES on the drawer in honest words naming the typed text (doorRefused, the persistent-banner seam from prod-post-loss-incident) and posts NOTHING -- a typed override is never dropped silently again (no-slop). NEW app/verify_onumparse.js 13/13 on the scratch instance (O-typed main/sub/part-list numbers take with source operator; bad text refuses and the posted program stands untouched; bare digits unchanged; lowercase o too); verify_refusal.js 16/16 green. The Sep-1 one-shot probe verify_onum.js (old URL, manual job id) stands as history. --- EXTENDED 2026-09-17 (Jordan: 'Where can I change the program number? We are out of sequence with our real shop, and remember, we should always have the option to override'): the PART-LIST post gets the same program-number door the single and nest posts carry -- run_plpost already took onum/onum_mode on sheet 1 (operator outranks the job's kept number; use-once vs new-sequence, reserved/used yellows ride as callouts) but the part-list Post tab never rendered the field and sent an empty body. Now: program-number input + override-mode select on the part-list Post section (app.js plOnumFields, placeholder = the job's current number), runPlPost sends {onum, onum_mode} when typed. Client-only + existing server door; seed never bind. --- ORIGINAL RECORD: Jordan 2026-09-06, his #6 on the 'Go on all' (proposal approved verbatim: 'record every sub number in the shop sequence file so nothing ever hands one out twice, and add a sub-number override on the Post drawer, recorded source:operator like the main'). BUILT 2026-09-07. Before this build subs were purely DERIVED (main+42000+k in angle order) -- two consecutive mains with two orientations each would COLLIDE (33339 -> 75339/75340 while 33340 -> 75340), and a program made outside kerfmaster could not be declared to the sequence at all. Now: server.next_subnums assigns every sub -- SEED stays main+42000 (the corpus convention, so existing jobs keep their exact numbers on the first re-post), but the auto counts past every number recorded in progseq.json ('subs' list), every used main (last + used_once), the reserved ranges (9000-series macro space, parked numbers) and the main itself; every number it hands out -- auto, kept or typed -- is RECORDED under the same lock the main sequence uses, so nothing is ever issued twice. A job KEEPS its subs across re-posts AND re-nests (stage record sub_onums, per sheet keyed by orientation -- _drop_postnest stashes them exactly like the mains; an orientation that survives a re-nest keeps its sub, a new one draws fresh). OVERRIDE: 'sub_onums' on POST /api/jobs/<id>/postnest maps orientation -> number (sheet 1, the main-number convention), recorded source:operator and kept thereafter; a collision with a recorded sub / used main / reserved range posts on the operator's call with a YELLOW riding in the NC as a header callout (warn never block -- the ruling-B precedent). Broken-program shapes are plain 400s in honest words, nothing saved: two subs sharing one number, a sub carrying the main's own number (the M98 would call the program into itself), junk value, an orientation the layout does not have (refusal names the layout's own orientations). The MAIN sequence knows the subs too: next_onum's auto counts past recorded subs, and typing one as a main warns it is a sub-program number. Both program styles ride it -- macro (P9997 subs) and part-by-part (G52 + M98 sub calls). UI: per-orientation 'sub number' inputs on the Post tab's nested-sheet section (placeholder = this job's current sub; an untouched field never flips source), per-sub source tags + collision yellows in the drawer. Proven: test_subnum.py 21 NEW controls (the old collision reproduced-and-gone, keep across re-post/re-nest, override end to end incl. the O90210 bytes in the posted file, refusal ladder verbatim, warnings riding in-file, main-sequence awareness both directions); neighbour suites green (progstyle 29 / seqexact 16 / seqcorner 17 / gseq 18 / sheetsize 20 / nestedit 38 / nestseq 27 / nc_warn 23 / nestenv 19). Honest limits: the override door is sheet 1's program (further sheets keep their tracked autos); pl_post part-list programs have no subs (part-by-part by construction, absolute one-program form); the pierce macro M98P9011 is a control-resident program, not ours to number. The nest-name-prog-number open question narrows: sub reserved-ranges handling is CLOSED (tracked + skipped); sequence SCOPE (shop-wide vs per machine) stays open with Jordan.
- •NC download filename = bare program number (33385.nc, no O) record — Jordan, 2026-09-17: 'When we download the GCode, the filename does not need the O at the beginning, so just 33385.' The O is Fanuc's program-word INSIDE the file; the shop's own corpus files are bare numbers (33199.NC, 33215.NC), so downloads now match the shop's filing convention. Applied everywhere an NC leaves the app: single-part post.nc, nested postnest.nc, common-line and part-list sheet program download buttons (app.js download= attrs + visible labels), and all three delivery writers (run_deliver / run_delivernest / run_pldeliver) name the shipped file {onum}.nc. Delivered versions are immutable, so pre-existing sets keep their O-prefixed names; new deliveries carry the bare number. Suites: pl_deliver 27 / nestdeliver_rot 14 / matrecord 30 / robust 50 green (robust check 40 expectation also refreshed for inject()'s added 'distinct' key -- stale from the 2026-09-17 grouping build, caught here).
- •Automatic true nest spaces on the TOOLING envelope; stamp replaces its own block; delete follows the entity record — BUILT 2026-09-05 on Jordan's three asks in one message -- his 'Yes.' on the standing question (does the AUTOMATIC nest's X spacing use the tooling boundary?) plus two hand-editor notes. (1) TOOLING-ENVELOPE SPACING on the automatic true nest -- the brick-3 bare-bbox honest limit closed: nest_rules.tool_envelope(bbox, plan, symmetric=) unions the part bbox with every reviewed-plan pierce point and lead end (nest-objective-function: the unit is part + lead + pierce envelope, never bare geometry; a segment's bbox is its endpoints', no sampling); run_nest spaces the grid, per-sheet capacity and pitches on the envelope -- 180-symmetrized about the part centre for the alt patterns so both orientations ride one regular lattice, the plain all-0 grid keeping the tight one-sided envelope -- and recorded placements stay part-bbox anchors inset by the lead room, so postnest array decomposition, the judge and every canvas read unchanged conventions. nest.json carries tool_env {w,h,pts,symmetric,source}; no plan points = bare bbox, source says so honestly. (2) STAMP REPLACES ITS OWN BLOCK (Jordan: rotate, stamp, change the repetition -- 'each step created a new nest on top of the old one. Maybe just keep one?'): stamp-made entities are tracked (draft E.stamped, persisted as layout_edit.stamped -- validated whole-number gids, pruned to the groups, junk = plain 400 nothing saved); re-stamping from a part of a stamped block deletes the block's other copies first and stamps fresh -- one stamp per base, never a stack; a packed or hand-made nest is never touched by the replace; undo snapshots carry the flags; survives Apply + reload. (3) DELETE FOLLOWS THE ENTITY (Jordan: deleting duplicates one at a time 'doesn't jive with our select-nest function' -- he was right): with no band selection, Delete/Backspace and the Remove button take the clicked part's WHOLE nest in the default nest-drag mode, one part in single-part mode; the button labels itself 'Remove nest'/'Remove part'; band selections delete exactly as before; every path snapshots for Undo. Proven: test_nestenv.py 19 NEW controls (envelope math incl. symmetric mirror, run_nest pitches = envelope + gap on a live fixture whose outer lead extends the box 4.0 -> 4.15, placements at margin + inset, capacity honest, stamped persistence + refusal ladder) + app/verify_stampdel.js 15 NEW real-mouse Playwright checks (auto nest records the envelope, stamp 2x2 -> re-stamp 2x3 = 9 parts never 12, replace survives reload, nest-mode Delete takes 6, part-mode Delete takes 1, undo restores); suites nestedit 38 / seqcorner 17 / handspread 27 / nestseq 27 / progstyle 24 / sketch 21 / customer 26 / nc_warn 23 / tooling_doc / rules 14 / drag 23 / tuck 22 green; verifies bandsel/groups/groupsnap/handsafe re-run green. verify_nestedit2 found STALE two builds deep (bare-bbox pitch expectation vs the snap-to-tooling seeds; a group-mode snap drag moving the whole stamp block), not a code defect -- updated to current semantics, 10/10. Server+engine live after the next restart (Jordan can /restart); the client half serves from disk immediately.
- •Seq display IS the program: the posted G-code executes exactly the order the interface shows -- NO EXCEPTIONS (0-tolerance, 'NO SLOP') ruling — Jordan 2026-09-06 on O33339 (second array cut bottom-up while the boxes showed 37 starting top-left): 'the GCode must be an EXACT representative of what we set in the interface: NO EXCEPTIONS! This software will be advertised as NO SLOP... If 37 is displayed as the second grid's starting point: top-left corner, then the GCode MUST reflect that. I will have a 0-tolerance policy with this.' The code follows the display, never the reverse; and 'the macro can absolutely reflect the displayed order -- if you post it correctly': negative J-values ARE accepted by the Mitsubishi ('Mitsubishi will read the code you write, let's make sure we write it correctly').
- •Tab consolidation -- one Geometry tab (sketch/intake/heal/QC), one Tooling tab (review+cut plan): Jordan's workflow shape; BOTH pieces BUILT record — Jordan 2026-09-06, verbatim: 'The purpose of my question to both of you: in my workflow, sketch, intake, heal, and QC would all be on the same tab with a full set of tools to insert or modify primitives. The cutting-edge that we would bring to the market is having the customer supplied source in a background layer so that we could always review whatever automatic healing, or manual editing against the original. I want to keep that, and build a user-friendly, comprehensive interface on it. Second, in my workflow, part review and cut plan would be the same tab. Again, a full stack of tools including sequencing, modifying lead-ins, tabs, offsets, condition classes, etc. all in one. Are there any flaws in my logic? If not, let's talk about doing this in the next build.' His use case for inserting on an uploaded job: 'If we have an existing job, and the customer says they want to add a hole or change one feature, but they don't have a cad file to give us, then this enables a quick solution.' GO given ('Why not both?' queued it behind per-nest sequencing; 'Go' 2026-09-06 started it). PIECE 1 BUILT same day -- ADD PRIMITIVES on any job: heal_edit add_line / add_circle / add_arc ops in the SKETCHER'S OWN typed forms (line by endpoints, circle by center + DIAMETER landing as the CIRCLE-diet semicircle arcs, arc by center/radius/start-end degrees), recorded on the heal-edit list, replayed verbatim on every re-heal (typed absolute coordinates -- nothing point-addressed to go stale), removable per row, refusals EditInvalid in the engine's words (zero diameter/length/sweep, junk numbers) plain-400 nothing saved; the pipeline's fidelity measure EXEMPTS deliberately added ink on the OUTPUT side (the delete-op exemption inverted -- the source never carried it, by the operator's design), the gray base layer keeps the untouched original, QC judges added geometry like any other (an open added line flags honestly), the standing stale rule fires on the add, and the added feature rides the NORMAL pipeline (review plans it, it posts). UI: 'add feature' block in the Heal/QC panel -- kind select, LABELED typed fields per the sketch-labels ruling, pick-position canvas click filling the coordinate fields (typed values stay the record), Add. Proven: test_healedit 29->42 engine controls + test_healadd.py 13 NEW server e2e controls + app/verify_healadd.js 12 live real-mouse checks on a scratch instance. PIECE 2 BUILT same day (Jordan 'Go'): the two tab MERGES, PRESENTATION ONLY -- client-side only (app.js/app.css, zero server/engine change; stages, gates, stale rules and records stay separate underneath). The strip is now Geometry / Quote / Tooling / Nest / Post / Deliver; a merged tab carries one dot PER constituent stage (per-stage status never hides -- strip, drawer sections and home job cards all keep stage-level dots); the Geometry drawer stacks Sketch (sketch-born jobs) + Intake + Heal + QC as sections each opening with its own dot+name, the Tooling drawer stacks Part review + Cut plan the same way. Review and cut-plan behaviors COEXIST on the one Tooling tab via stage-coverage guards (onTab): drag-handle/pick-start/band-select run as on review, Manual Sort click-in-order as on cutplan, a plain click selects a contour on either scene; the canvas draws the cut-plan scene when the sequence is judged and the review scene otherwise (the post/deliver precedent -- a re-plan drops the judgment and the scene follows). A sketch job's Geometry tab draws the TYPED preview until the sketch is applied and current (sketchPreviewOn: not-done or stale), then the healed geometry -- the compiled sketch -- is the on-screen truth and heal-edit/dimension tools work on it. Old #tab=heal / #tab=cutplan deep links land on the covering tab (tabAlias -- verify scripts and bookmarks keep working); sales lens strips to Geometry + Quote (same stages as the ruled set); firstAttentionTab walks STAGES in pipeline order and opens the covering tab; the tour's stage step teaches the merged vocabulary. Proven: app/verify_tabmerge.js 20 NEW live real-mouse Playwright checks on a scratch instance (merged strip, per-stage dots on strip/drawer/home card, all four old deep links, review plan + cut plan judged + contour click + Manual Sort on the one Tooling tab, sales lens, sketch preview->healed switch); test_sketch 21 / test_healadd 13 green (server untouched by construction); Jordan's live job renders intact over the public URL. Client serves from disk -- LIVE IMMEDIATELY, no restart needed.
- •Busy feedback on EVERY slow action -- any api call still running after 600ms raises the processing overlay, with elapsed seconds ticking; BUILT record — Jordan 2026-09-09: 'Give the user visual feedback: when the button takes about a minute to do its job, somehow indicate that on the screen.' BUILT same day, client-only (app.js -- serves from disk, live immediately). ONE SEAM instead of touching every handler: fetch itself is wrapped -- any POST to our api still pending after a 600ms grace raises the SAME processing overlay the 2026-09-04 build added for uploads (units-confirm-modal), with honest per-endpoint words (healedits 'Applying geometry edits -- re-heal, QC, downstream re-check...', nest, post, deliver, partnest, vecextract, sheetextract, quote... ; unknown endpoints say 'Working...'); fast calls never flash it. After 5 seconds the card ticks elapsed seconds ('-- 12s') so a minute-long job visibly stays alive. dragcheck is EXCLUDED (the live drag judge fires under the mouse -- an overlay would eat the drag); GET healmarks is INCLUDED (a first lens click on a big file computes). busyShow/busyHide are now show-COUNTED so the explicit sites (upload, units flip, vector convert) compose -- the wrapper defers when an explicit indicator already speaks. The modal overlay doubles as a double-click guard while a pipeline re-run is in flight. Proven: app/verify_busy.js 14 live Playwright checks on a scratch instance (route-delayed calls prove every timing branch deterministically: show/hide, 600ms grace, dragcheck exclusion, seconds tick, explicit-compose, fallback words, healmarks GET, plain-GET exclusion, real-call round trip) + verify_units.js 16 re-run green (the explicit-overlay flows unchanged).
- •Multi-file upload + whole-window drag-and-drop on the Jobs home; BUILT record — Jordan 2026-09-09 (screenshot of the single-select browse dialog): 'When we are uploading a file, we can only select one at a time. I would also like to have a feature where we can just drag and drop our files in, rather than having to browse and select.' BUILT same day, client-only (index.html + app.js -- serves from disk, live immediately, no restart). The browse input takes SEVERAL files (multiple); the drop target is the WHOLE WINDOW, not the dropzone box -- dropping anywhere uploads (only real OS file drags, dataTransfer type Files, so panel/canvas drags are untouched; the dropzone keeps its highlight as the affordance). A batch uploads SEQUENTIALLY -- one pipeline run at a time, per-file progress on the busy card ('2 of 6: name -- intake -> heal -> QC...'), then the Jobs list refreshes in place and ONE summary toast counts the landings and names every refusal in the server's own words (a refused file -- DWG, SVG, junk -- never stops the rest; the format door rules of upload-formats-door apply per file unchanged). A SINGLE file keeps the exact old flow: its job opens when it lands. Same-file re-pick fires again (input value cleared); long refusal summaries hold the toast up to 12s. Proven: app/verify_multiupload.js 11 live Playwright checks on a scratch instance (multi-batch, stays-home, single-opens, drop on the job LIST far from the dropzone, DWG refusal named while the good file lands). Three-lens clean: plain DOM input[multiple] + drag events, no browser-only API.
- •View/intake extents measure TRUE arc sweeps, never full-circle padding; FIXED record — Jordan 2026-09-09 (7497.dxf screenshot): pressing 'f' fit the part as a speck at 0.4% zoom and the Intake drawer read extents 1280.646 x 2535.75 on a part whose real ink spans 115.181 x 58.090 -- 'we thought that maybe there was either an open contour, tiny segment etc. out at the edge... nothing to see. Why?' He was right to trust the lenses: NO stray geometry existed. The defect was ours: server._extents padded EVERY arc by its full circle (center +/- radius, both axes), and 7497 carries 1140 native arcs whose nearly-straight edges are drawn as giant-radius arcs -- the worst a 640.32-inch radius under a 0.19-inch chord, single-handedly inflating the box by its whole 1280-inch circle. The same padding was already known WRONG for nesting (part_bbox, brick 3) but the code comment claimed it 'fine for view fit' -- Jordan proved it is not. FIX (his 'Go', same day): _extents now delegates arc extremes to emit._arc_extremes -- the SAME cardinal-point arithmetic the slug-size bbox has always run (an arc reaches center +/- radius on an axis only when its sweep passes that compass point; coincident-end full-circle segs still box whole) -- ONE arithmetic, never a second that could disagree with the slug gate. Fixes the Intake extents line AND the 'f'/fit box in one place (both read job.extents). Proven: test_view_extents.py 10 NEW controls (the giant flat arc boxes as chord + sag; CW/CCW representations agree; mid-quadrant arc = endpoints only; two-semicircle circle diet; the REAL 7497 source segs measure 115.1813 x 58.0896 exactly) + neighbour suites green (healadd 13 / healedit_noop 8 / units_confirm 27 / introt 24 / sheet_intake 36 / upload_formats 17 / sketch 21 / emit_extents). Jordan's live 7497 job record recomputed under the job lock via the new codepath -- extents now equal part_bbox to the digit (two independent arithmetics agreeing), serving live immediately. Server-side change: NEW uploads get true extents after the next owner-turn restart; older jobs correct on their next re-heal (extents is a display/record value -- nothing downstream drops). WRITER HEADER JOINED 2026-09-15 (the same defect class found in emit.write_dxf_metacam by Jordan's stacked part-list nest): the MetaCam writer's $EXTMIN/$EXTMAX padded every arc by its full circle -- a split child carrying 7497's giant flat arcs wrote a header ~25x its ink and to_inches' magnitude inference read the written file back as mm. The header now runs emit._arc_extremes like the view fit and the slug bbox -- one arithmetic everywhere. test_view_extents 8->9; full story on pin units-confirm-modal.
- •Toolpath planner performance pass: chunk bbox prefilter under the clear-air scans -- 11.5 min to ~70 s on 7497, output bit-identical record — Jordan 2026-09-09 ('Is this stuck or is it really taking this long?', the 116-feature 7497.dxf; his 'Go'): the review plan ran 11.5 minutes (started 15:40:53, review.json landed 15:52:23) with one core pinned. Measured cause, not guessed: ~2000 _pick_lead calls (116 boundaries x up to 18 start candidates), and inside each probe emit._ray_clear and emit._pierce_clear scanned EVERY segment of the whole flattened drawing -- tens of thousands of segments touched per probe on a 115-inch sheet where a lead reaches a fraction of an inch. The fix is a PREFILTER, never a second arithmetic: emit._chunk_polys builds a per-poly bbox index (32-segment chunks) once per post; _ray_clear gates polys on a padded slab test (_ray_box) then chunks the same way, _pierce_clear gates polys and chunks on padded squared point-to-box distance. Every rejection is conservative by construction -- a chunk is skipped only when nothing inside it could better the running answer -- so the picked lead, its length, the pierce, the tab span and every finding are bit-identical with the index on or off. PROVEN both ways: test_plan_perf.py 6 NEW controls (whole-plan JSON identity, chunk partition, slab-test conservativeness on a 500-ray seeded sweep, _ray_clear/_pierce_clear exact equality, degenerates); chunked-vs-unchunked identical on the real nav209 / 44558B_10 / b7787 jobs; the new engine's 7497 plan compared field-for-field IDENTICAL to the live tooling.json the old engine spent 11.5 minutes writing. Measured: 7497 plan 660 s -> 61 s (plus 10 s heal), nav209 1.19 -> 0.37 s. 16 neighbour suites green (drag 23 / dragcheck 15 / lead_tab_comp 22 / tooling_doc / rules 14 / nc_warn 23 / sketch 21 / plan_display / dcond / header_notes 22 / progstyle 29 / seqexact 16 / pl_post 26 / cl_post / traverse 10 / nestedit 38). Honest limits: the planner stays single-threaded -- the remaining ~60 s on a 116-feature sheet is Python loop floor; spreading candidate starts across the box's 4 cores (~4x more) is the recorded next step if big sheets stay slow. Engine change: live after the next restart.
- •Planner multi-core fan-out BUILT 2026-09-17 (7497 plan 61.5 s -> 27.3 s, 2.25x, whole-plan identity proven on real fixtures); the full ratified five-lever performance conversation (Aristide 2026-09-15) stands recorded below: bigger box = money question, PyPy = measured trial, numpy/C rewrite parked, caching mostly done recordmixed — BUILT 2026-09-17 (Aristide 'ok, now start on the core split up'): STEP-0 RE-PROFILE ran first, per the spec's own stop condition -- cProfile on the real 7497 source: _choose carries 114.2 s of the 115.5 s profiled plan (98.9% cumulative), _cut_order absent from the top 30 -- the target had not moved, the build proceeded. THE SEAM AS SPECIFIED: the picks loop now runs through emit._parallel_picks(choose, n, parallel) -- fork-start multiprocessing Pool (Linux, stdlib, no new dependency); _choose is a closure and not picklable, so it is stashed in module global emit._PAR_CHOOSE and forked children inherit it copy-on-write, running the top-level _par_choose trampoline; pool.map collects in index order, so the parallel plan IS the serial plan -- same arithmetic, no second implementation. GUARDS as ruled: engages only at >= 12 boundaries (_PAR_MIN -- small parts and the throttled dragcheck judge stay serial); workers = min(3, cores-1, n); a parallel= kwarg on write_gcode_macrob (None=auto, False=forced serial, True=engage at any size) rides through rules.check_drag's **post_kwargs untouched; ANY pool failure falls back to the serial loop COUNTED in stats['parallel_picks'], never silent; a parent-side threading.Lock serializes concurrent stash+fork from the threaded HTTP server (children stay lock-free pure compute -- the ruled fork-safety guard); the outer approach= re-pick stays serial per spec. GATES (the plan-perf-pass discipline): test_plan_perf 6 -> 10 controls -- parallel-vs-serial WHOLE-PLAN JSON identity (stats['parallel_picks'] popped and asserted separately -- the one legitimate difference) on the synthetic fixture; a threshold-path control; a forced-failure fallback control (module-level raising stub, pickled by reference); real-source identity on nav209 / 44558B_10 / b7787 (serial -- under threshold) / 7497 (parallel, identical). Nine neighbour suites green (drag 23 / dragcheck 15 / lead_tab_comp 22 / tooling_doc / rules 14 / nc_warn 23 / pl_post 26 / cl_post / traverse 10). MEASURED: 7497 plan 61.5 s serial -> 27.3 s with 3 workers (2.25x) -- inside the honest Amdahl expectation below (ordering, emission and audit stay serial; concurrent big-sheet plans serialize on the stash lock by design). Engine change: kerfmaster-app restarted, live smoke on dev.kerfmaster.com. Levers 2-5 stand as recorded. --- THE ORIGINAL RATIFIED CONVERSATION (2026-09-15): NOTHING BUILT YET -- this pin is the WHOLE ratified performance conversation (Aristide 2026-09-15, 'agreed with recommendation' then 'document the whole conversation, not just the planner fan-out'): all five levers with the pros and cons as discussed, PLUS the full execution detail for the fan-out night build, written so a fresh session after compaction can execute it without re-deriving anything. It closes the where-does-the-time-go discussion after the 62-suite full test. SLOW SPOTS TODAY: big-sheet toolpath plan ~60 s on 7497's 116 features (the Python loop floor left after pin plan-perf-pass); first heal of a big file ~6 s then cached; a few test suites 30-50 s each because they run whole pipelines over HTTP; Playwright verifies 30 s - 3 min of real browser time. THE FIVE LEVERS, PROS AND CONS AS DISCUSSED. (1) MULTI-CORE PLANNER FAN-OUT (build on trigger, spec below). PRO: stdlib multiprocessing on the box's 4 cores, no new dependency; bit-identical by construction and PROVEN by gates (same arithmetic, just concurrent -- no second-arithmetic risk); ~a day of work; hits the slowest thing Jordan actually waits on (big-sheet plans). CON: Amdahl -- only the boundary-picks loop parallelizes, ordering/emission/audit stay serial, so expect ~2.5-3x on the picks share (7497's ~60 s lands roughly ~25-30 s, not 15); forking from the threaded HTTP server needs the pure-compute-worker guard; the box is shared with the bridge, so workers cap at 3. (2) BIGGER DEV BOX. PRO: the ONLY lever that meaningfully helps the test/verify sweeps -- those suites are slow by nature (full pipelines over HTTP, real-browser Playwright) and parallelizing them on this 4-core / 7.6 GiB box would thrash it; more cores also raise the fan-out's ceiling; zero code risk. CON: money, and a platform migration question (provider side) -- Aristide decides separately; on its own it does not fix the single-threaded Python floor. Nothing to code. (3) PYPY TRIAL -- a quiet-turn measured experiment AFTER (1). PRO: the engine is pure-stdlib Python, the ideal PyPy case -- typically 3-7x on loop-heavy code with ZERO code change; multiplies with the fan-out rather than competing with it. CON: a second runtime to keep honest between dev and prod; JIT warm-up penalizes short runs; nothing is trusted until the gate passes -- run the full suites under PyPy and diff real-job plan/NC outputs byte-for-byte against CPython (proof, never assumption). (4) NUMPY/C REWRITE OF HOT LOOPS -- PARKED, recommended against. PRO (why it tempts): the biggest raw speedup potential on the geometry inner loops. CON (why it parks): it creates a SECOND ARITHMETIC beside the one every gate and audit shares -- construction and judgment must run the SAME arithmetic, and the drift risk buys back the exact class of bug our controls exist to prevent; plus a new dependency and a large rewrite while levers 1-3 are still unspent. (5) MORE CACHING -- mostly done already (heal cache keyed on source+tol, plan reuse, the plan-perf-pass chunk prefilter); the remaining wins are small, not worth a build. THE RATIFIED RECOMMENDATION, in order: go on (1) -- a day of work, provable, helps Jordan directly; decide (2) as a money question -- it is the only fix for slow test/verify sweeps; trial (3) as a measured experiment some quiet turn; park (4). THE FAN-OUT BUILD SPEC. STEP 0, RE-PROFILE FIRST: the 61 s on 7497 predates this build -- time the picks loop vs _cut_order vs emission (perf_counter on a scratch plan of the 7497 source, jobs/7497-1789412543/source.dxf); if ordering now dominates, STOP and report -- the target moved. THE SEAM: emit.py ~line 3039, picks = [_choose(i) for i in range(len(cons))] -- each _choose(i) reads only cons/depths/polys/pboxes/pchunks/tooling/overrides, all frozen by that point, and boundary picks never read each other (verified in code 2026-09-15; the outer re-pick with approach= at ~3080 runs AFTER the ordering, serial, outers only -- leave it serial). MECHANISM: multiprocessing with the FORK start method (Linux, stdlib, no new dependency; threads are useless here -- GIL). _choose is a closure, not picklable -- the fork trick: stash it in a module global (emit._PAR_CHOOSE = _choose) immediately before creating multiprocessing.get_context('fork').Pool(workers); children inherit it copy-on-write and run a top-level _par_choose(i) that calls the global; pool.map preserves index order; the returned pick dicts (prim tuples, floats, bools) pickle fine. DETERMINISM: same CPU, same code, index-ordered collection -- bit-identical by construction, and PROVEN not assumed (gates below). GUARDS: engage only at len(cons) >= ~12 boundaries (small parts stay serial; dragcheck's throttled live judge stays cheap); workers = min(3, cpu_count()-1, len(cons)) so the box -- bridge and server share it -- never pegs; a parallel= kwarg forces serial (tests compare both paths); ANY worker failure falls back to the serial loop, counted in stats, never silent; the worker path stays lock-free (no logging, no imports inside _choose) -- we fork from the threaded HTTP server, pure-compute children are safe, a held lock would not be. GATES (the plan-perf-pass discipline): test_plan_perf extended with parallel-vs-serial WHOLE-PLAN JSON IDENTITY on synthetic fixtures AND the real nav209 / 44558B_10 / b7787 / 7497 sources; a forced-failure fallback control; a threshold-path control; neighbour suites green (drag / dragcheck / lead_tab_comp / tooling_doc / rules / nc_warn / pl_post / cl_post / traverse at minimum); measure 7497 wall time before and after and report the real number; sudo systemctl restart kerfmaster-app; live smoke on dev.kerfmaster.com. EXPECTED HONESTLY: ~2.5-3x on the picks-dominated share of a big sheet (Amdahl -- ordering, emission and audit stay serial); state the honest limit wherever the number lands. TRIGGER: Aristide or Jordan saying 'Go: planner fan-out' (or any clear go naming the planner fan-out or this pin) starts the build; this pin flips to record in the SAME commit as the code, per the standing rule.
- •NC-audit performance pass: mechanical speedups in the parse/grid/annotate/geometry hot paths -- reports byte-identical, ~1.6x on the heaviest real programs record — BUILT 2026-09-18 (Aristide '5' on the standing queue; the go was on record). Profiled first, never guessed: cProfile on the 3192-boundary expanded macro sheet (O33289) put the time in pierce_clear (Grid build over ~2.2M flattened chord segments + per-pierce nearest), rules.annotate (bbox/area/point-in-poly over the same flattened contours), parse_nc's per-line regex builds, and the audit's geometry-vs-source candidate scan. EVERY change is mechanics, never arithmetic -- the same expressions evaluate in the same order on the same operands, so every distance, every finding and every reported number is bit-identical by construction AND by proof: (1) parse_nc precompiles its X/Y/I/J and motion patterns; _arc_pts becomes a comprehension of the identical per-point formula; (2) Grid.__init__ computes each point's cell ONCE and reuses it across adjacent segments (floor is monotonic, so the segment's cell range is exactly the min/max of its endpoint cells), with a fast path for the single-cell chords that dominate; bucket contents and their order are unchanged; (3) Grid.nearest scans only the NEW perimeter cells of each ring (a rescan can never win under the strict d<best gate; new cells visited in the same dx-major order, so ties resolve identically) with the _seg_dist arithmetic inlined verbatim; (4) rules._signed_area/_point_in_poly drop their per-edge list copies and indexing, same terms in the same order; annotate reuses the bbox it just computed; (5) the audit's geometry check breaks out of a sampled point's candidate scan as soon as one distance drops to <= the running worst -- the update gate is strictly >, so worst/worst_at land identical to the full min, and on a clean program almost every point sits ON its first candidate. PROVEN: full audit reports (anomalies+info+findings, sorted JSON) byte-identical old-vs-new on six real programs -- the O33289 3192-bnd P9997 macro (18.65 s -> 12.00 s), O33286 1140-bnd (2.81 -> 1.79), the CAMP part-list sheet O33342 with placed source + Rev2 tooling (2.45 -> 1.52), a single-part post, the brazen legacy program, tl101681 (1.35 -> 0.81); PLUS a distorted-source case (scaled 1.001) that exercises the cut-path-leaves-source anomaly branch, old modules from git HEAD vs new in the same interpreter inputs -- IDENTICAL including geom_max_dev 0.0502 and the worst_at point. Suites green: nc_warn 25, warns 20, rules 14, drag 23, dragcheck 15, tooling_doc, pl_post 26, cl_post, traverse 10, seqexact 16, pl_sync 23. The savings DOUBLE at every posting door: nc_warn.audit_and_inject audits twice by design (the transparency recheck gate stands untouched). Honest limit: the remaining time is the pure-Python floor of flattening arcs at the ruled ARC_STEP 0.004 and building the segment grid -- a further big step means PyPy (the ratified quiet-turn trial) or coarser sampling, which would CHANGE measured numbers and is not on the table.
- •Treat-as-text BUILT + REFINED (one gesture: comp off + curve-run arc consolidation, true corners stay SHARP) AND the deterministic AUTO-DETECTOR BUILT (measured signatures propose a text group, one-click ratify fans the same gesture -- seed never bind) record — Jordan 2026-09-09 (7497 screenshot, verbatim): 'Is the codeset able to recognize text features and remove beam comp automatically without the use of AI? How can I select a set of features and indicate to treat is as text? If it is text, then I want to round the arcs and take out beam comp.' BUILT 2026-09-09 on his 'Continue' go; SEMANTICS CORRECTED same day by his letter-R spec (verbatim): the R 'would have on the top left corner a 90-degree corner, and on the bottom of the vertical stick on the left side of the R it would have two. These corners should not be "smoothed" but the half-circle that makes up the upper right should have at minimum one arc, but depending on the font it could have multiple tangent arcs.' So emit.round_text_corners is CURVE-RUN ARC CONSOLIDATION, never corner filleting (the first build's fillet-every-corner reading is retired): true corners stay sharp -- the healer's own neighbor-consistency rules (classify_corners constants) applied at the prim-junction level with arcs carrying real end tangents, a lone ~90-deg break ALWAYS a corner -- and each corner-free run re-fits into the FEWEST tangent arcs: one true arc through the run's exact endpoints first (his 'at minimum one arc'; center = the healer's least-squares circle snapped onto the endpoint bisector), heal.fit_biarc_run as fallback. This folds both raw line facets AND the many small wiggly arcs healing threads through text facets (the in-app form -- proven: a 16-facet sketch bowl healed to 6 arcs consolidates to ONE, worst dev 0.0023). Budget = the audit's own geometry gate max(0.005, beam_comp); every fit MEASURED both ways on parametric samples (a _poly-based measure aliased onto facet vertices and read 0.0 -- caught and replaced); a run whose fit misses the budget, grows a prim under beam comp, or needs AS MANY prims as it replaces keeps its source form -- which also preserves clean geometry exactly (circle diet, stadium: byte-identical, proven). Per-feature override text:'on' in tooling_doc.CONSUMABLE; record carries text ran+source + arcs-fitted count + worst deviation. Comp stays its own knob: the UI gesture (review contour panel, fans over band-select/twins via ovApply) sets {text:'on', comp:'off'} in one click -- the recorded G40-on-text convention -- clear takes both back; pl_post + the doc round trip replicate per copy. Proven: test_text_treat.py 27 controls + verify_texttreat.js 12 live real-mouse checks (square hole marked text keeps all four 90s SHARP end to end; the faceted D bowl consolidates to one arc through the real heal pipeline). STILL INTENT (the mixed flag): the deterministic no-AI DETECTOR -- measured signatures (small closed contours, arc-heavy, similar height band, collinear row, nested counters) PROPOSING a text group for one-click ratify, seed never bind -- is the next block, commissioned by his 'Continue, using logic' 2026-09-09. DETECTOR BUILT 2026-09-15 (Aristide 'go in that order: First' on the standing go): engine text_detect.py (docstring = design record) -- PROPOSE NEVER APPLY, measured signatures only, no AI: closed chains cluster by agreeing size (bbox max dim within 40% of the group median), ONE straight baseline in any direction (PCA centroid fit within 0.35 x size -- rotated and vertical words detect; curved/multi-line text = the recorded honest limit), letter spacing (2.0 x size along the line), and >= 2 DISTINCT congruence signatures via emit._congruence_sig (the ONE identical-shape arithmetic) -- so bolt rows, slot arrays and different-drills-on-a-line NEVER read as text ('III'/'OOO' do not detect either: the conservative side of seed-never-bind); an off-baseline straggler sheds one at a time, so a hole beside a word costs the hole its membership, never the word its proposal; a chain CONTAINED in a member (the A's counter) rides its letter's group and never proposes on its own; every threshold is a proportion of the group's own measured size, never a machine number. SERVER GET /api/jobs/<id>/textdetect -- READ-ONLY (the dragcheck pattern): the same edited prims every stage consumes, nothing persisted, no stage record; a part list refuses in honest words. UI: 'Text detected (proposal)' block on the Part review drawer -- the engine's measured note verbatim, mark-all-as-text / select / dismiss; the ratify IS the recorded gesture automated (select the word, setText) so ovApply fans one per-feature entry per member ({text:'on', comp:'off'}, source:operator, bound to its own centroid); dismiss is session-only presentation state, stated on-screen; a ratified group hides itself by its members' own record. Proven: test_text_detect.py 23 NEW controls (every gate one by one + HTTP e2e: read-only, no stage flip, refusal ladder verbatim) + app/verify_textdetect.js 15 NEW live real-mouse checks over the public URL (nothing applied before the click, dismiss session-only across a reload, ratify fans 4 features to source:operator, persistence across a fresh page, bolt-row control proposes nothing; scratch fixtures removed); neighbour suites green (text_treat 27 / tooling_doc 37 / drag 23 / dragcheck 15 / rules 14 / sketch 21). Both halves of the 7497 ask are now record -- the mixed flag is cleared.
- •Aristide's interface visibility list (2026-09-11) — all 9 items BUILT same day record — Aristide, 2026-09-11, screenshots on the RJB job: 'a few interface issues I would like to address... Just take inventory. you can then execute on my mark.' The list: (1) panel-header icons (grip dots, collapse dash, tear-off arrow) too small — make larger. (2) Left tool palette: the one-letter key labels (V/F/O/H/J/B/N) too small to read; the layer-toggle buttons all share one circle-dot icon and one color — give each a DISTINCT icon from a real icon library (not emojis), and tint each layer button with the color the user assigned to that layer in Colors & legend (picker changes follow onto the button; V/F tools stay neutral — no layer color). (3) Repeated findings group by KIND under a chevron: rows differing only by which feature they hit collapse to one row per kind with a count ('pierce clear air 0.1311 < 0.15 standoff — 6x'), chevron opens to the individual rows which still navigate (ui-warnnav-brick17); collapsed by default; the RED/YELLOW banner keeps the true total. His RJB case: 8 yellows -> 2 rows (0.1258 x2, 0.1311 x6). (4) Numbered sequence callouts on the canvas too small AND they shrink with zoom-out into the geometry — hold a readable SCREEN-FIXED size at any zoom. (5) Dimension value labels ('6X R0.1330') same treatment — readable screen-fixed size. (6) Move dimension numbers to any arbitrary spot — ALREADY BUILT (drag-to-place, 2026-09-09, pin ui-dims-brick27): verify it works for him; the tiny grab target is likely the real obstacle, fixed by (5). (7) One-click SCREENSHOT of the visible canvas only (never the toolboxes/panels), two modes: copy to clipboard, or save via a file dialog the operator navigates (three-lens note: Chrome/Edge have a real save-anywhere picker, Firefox/Safari fall back to a named download honestly; native shells get the native dialog; clipboard works on all three). (8) Recorded-edit rows in the Heal/QC panel ('Drop duplicate lines' + tiny x) read as a clarifying note, not an actionable item — give each recorded edit a proper actionable row treatment (card/bordered, distinct from the gray explanatory text) with a clearly visible remove button instead of the tiny x. (9) Dimension-kind buttons crowd edge to edge — the active button's highlight ring overlaps its neighbors (his Diameter screenshot); space button groups so the active state never overlaps, or keep the active style inside the button footprint (applies to any group using the ring style). NOTHING BUILT YET — execute on his mark. BUILT 2026-09-11 on his mark ('go ahead and execute'), client-only (app.js/app.css/index.html — serves from disk, live immediately, no restart): (1) panel-header grip/collapse/tear-off icons 10->15px, buttons 12->16px. (2) tool-palette key letters 11px bold; every tool button carries a DISTINCT inline SVG icon vendored from Lucide (ISC — move/maximize/file/spline/alert-triangle/target/route, no emojis, no runtime CDN); each layer button is tinted by its own Colors&legend CSS variable read live (picker changes follow; V/F neutral; off-layers dimmed). (3) findrows(): findings grouped by KIND (severity + message with feature numbers/coordinates normalized away — measured VALUES stay distinct, his 8->2 example) under a <details> chevron showing 'SEV ×N'; rows inside still navigate (brick 17); open-state survives the re-render a child click triggers (S.fgOpen); singles render flat; banners keep true counts; wired on Part review, Cut plan, Post (single + nested — nested rows stay honestly non-navigable, renumbered expanded bnds). (4) canvas sequence callouts = screen-fixed circled badges (panel disc, severity/selection ring, bold 13px — the drawer's own vocabulary) on the cut plan; review labels bold 13px with a panel-color halo; seq-pick badges and both nest numbered-box renderers enlarged (bold 12px, 20px boxes) — all already screen-fixed, now readable. (5) dimension value labels 13px in a padded bordered chip. (6) drag-to-place verified working; the grab target grew with the label (±9px pad) — the tiny target was the obstacle, as suspected. (7) two canvas-only screenshot buttons in the zoom bar (icons swapped to camera glyphs 2026-09-11 -- Aristide missed the copy/save forms: camera = copy, camera-with-down-arrow = save): copy-to-clipboard (ClipboardItem, honest toast when unsupported) and save (showSaveFilePicker real dialog on Chrome/Edge; Firefox/Safari fall back to a named download and SAY so; canvas element = panels excluded by construction). (8) recorded-edit rows are bordered cards with a visible 'remove' button replacing the tiny x. (9) active-button ring moved inside the footprint (inset box-shadow) + spacing between small buttons — no overlap in any group. Proven: app/verify_visibility.js 15 live Playwright checks over the public URL (7497's 9 yellows -> 2 groups + 5 distinct-value flats, all 9 accounted, child-click navigation with the group staying open, 7 distinct SVG icons with 5 layer tints, real PNG capture 22KB, ex2 cut-plan badges clean, b7787 edit card + remove, dims job clean). Screenshot spot-check on ex2's cut plan confirms badges/tints/buttons visually. FOLLOW-UP FIX 2026-09-11 (Aristide's zoomed screenshot -- 'seems aligned too far to the right'): the item-1 icon enlargement made the 56px Tools panel header overflow -- measured scrollWidth 65px vs 55px available, the tear-off arrow clipped ~10px off the right edge and the row read as crammed right. Fix = tighter padding on THIS narrow header only (app.css: tools pgrip 5px 4px gap 4px, tools pbtn 0 2px; icon sizes untouched -- readability stands): all three glyphs now fit (arrow ends at x=51, zero overflow docked AND floating; collapsed strip + wide panel headers unaffected, verified by screenshot). Client-only, live immediately. ITEM-3 GAPS CLOSED 2026-09-11 (Aristide's screenshot ask, 'the nesting of these doesn't carry across tabs. is that possible?'): four finding lists still rendered FLAT outside the grouped renderer -- the Nest drawer's hand-layout judge findings (12-row cap) and the part-list Nest drawer's per-part / sheet-wide / per-program audit findings (4/8/4-row caps) -- all four now run findrows() like review/cutplan/post, so the nesting carries across every tab; the caps go (a same-kind group folds to one chevron, so full lists render without flooding -- the no-silent-caps spirit), flat=true keeps nest-side rows honestly non-navigating (their coordinates live in sheet/part frames, not review contours). Proven: app/verify_findnest.js 8 live Playwright checks (the screenshot's exact 8 yellows fold to x2+x6 through the shipped renderer, Lathrup part-list rows intact, zero page errors). Client-only, live on refresh. NOTE: the screenshot's own Tooling tab already groups on the current client -- a flat Tooling list means that browser tab predates the morning build; refresh.
- •Windows installable version — preliminary challenge inventory intent — Aristide, 2026-09-13: 'this is all preliminary right now' — the challenge inventory for a PC-installable kerfmaster, documented on his ask. Nothing commissioned; the two strategic decisions need a sitting with Aristide + Jordan before any building.
- •kerfmaster.com security page — DRAFT exists, publish blocked on Aristide's checklist record — Aristide, 2026-09-13: a public security page for kerfmaster.com, adapted from his MyVideoTracker template to what is TRUE of kerfmaster today. The draft lives at docs/security-page-kerfmaster.md — deliberately OUTSIDE the auto-deploy dirs so nothing ships until his word.
- •Split into parts — a multi-part file becomes one job per part (BUILT 2026-09-14) record — Jordan, 2026-09-14 (his 7497/7498 part-list screenshot, each sheet reading 1 pcs): a DXF carrying a whole kit was packed as ONE giant rigid part. His ask, from the August TL-1016 precedent: split them into individual components. Go given same day (Go on all). NEST DOOR BUILT 2026-09-15 (Jordan's go: 'I need this feature to complete my job here... build what you think matches our overall goals'): 'Nest these parts...' button ON the split record (Geometry tab, Intake section) -- one click creates the part list from the split children, files members inside the list's home group (the grouped-upload-tray shape) and lands on the Part list tab; the operator picks machine x material there, then Save list + pack sheets. AUTOMATIC QUANTITY GROUPING, his 2a ruling in the same message ('if some parts are 100% identical (all contours considered), then why wouldn't we group those together as an increased quantity automatically?'): 100%-identical parts fold into ONE row with qty = count. The arithmetic is NEW engine split_parts.congruent_parts -- whole-part rigid congruence, NEVER outer-profile-only: one rotation + translation must map EVERY contour (interior cutouts AND their positions) within PART_MATCH_TOL (0.005 in, a construction margin over healing scatter, never a machine number); verification measures densely sampled points against the other part's EXACT prims so identical holes healed with different arc splits still group; MIRRORS NEVER GROUP (rotation-only search -- a mirrored copy fails unless the part is itself symmetric, where the mirror IS a rotation and grouping is correct). Rotated-in-the-drawing copies group (a part's identity never depends on its drawn orientation; the pack's Rotate column stays what the PACK obeys). GET /api/jobs/<id>/partgroups serves the grouping over the living split children -- READ-ONLY (the dragcheck pattern), refusals plain-400 in honest words (not split / no healed children); unhealed children left out NAMED, never silent. Proven: test_partgroups.py 21 NEW controls (hole-MOVED variant never groups -- Jordan's exact inside-cutout concern; sizes-swapped, mirror, arc-split, 2-thou-groups/10-thou-splits, read-only byte-identical) + app/verify_splitnest.js 10 live real-mouse checks over the public URL (3 identical + 1 hole-moved variant -> 2 rows qty 3 + 1, home-group binding, fresh-page persistence; scratch removed); split_door 17 / part_list 28 / isoserver 17 / healedit_noop 8 green. Live check on his real kit: 7497 9 parts + 7498 5 parts = ALL genuinely distinct, zero false folds -- matching his own statement that they are not identical.
HISTORY (the gap as recorded 2026-09-15 morning): POST-SPLIT NEST DOOR -- OPEN GAP (Jordan 2026-09-15, on his CAMP#4 sheet: 'When I click the tool to split these into individual parts, how do I navigate the workflow to get to the nesting stage? I'm still not seeing this in the interface.'): the split lands the children on the Jobs home and lists them in the Intake section, but no door leads ONWARD -- the operator must know to go Home > New part list > machine x material > 'add all parts from: <file>' > Save list + pack, or (identical parts) open ONE child and true-nest it from its own Nest tab with the sheet-size door. OFFERED (awaiting a go): a one-click 'Nest these parts...' button on the split record itself -- creates the part list, adds one row per child, opens the Part list tab; client-only over the standing endpoints. Recorded same turn as born, nothing built yet.
- •Annotation ink (dimensions/text) visible to the operator -- how to spot a dimensioned preview part (Jordan 2026-09-15) record — Jordan, 2026-09-15, on his CAMP#4 pre-nested customer sheet (91 parts + one lone dimensioned 'preview' copy in the upper-left, known only from his own DXF readers): 'How would the operator know that the upper left part was a dimensioned preview if all of the source dimensions and outlines are not visible?' HONEST STATE TODAY: dxf_io.parse never reads DIMENSION/TEXT entities (they are not cut geometry), so the gray base layer cannot draw them; the ONLY tell is the sheet-intake yellow's counts ('2 dimension entities, 16 text entities, 434 separated view clusters' on his file) -- the operator learns the file CARRIES annotation ink, never WHERE it sits. OFFERED (awaiting a go): (1) parse DIMENSION/TEXT ink for DISPLAY ONLY and draw it on the gray base layer -- the customer's original stays the review-against-source spine, annotation ink rides it dimmed, never healed, never cut; (2) mark the cluster that OWNS the dimension entities on the canvas ('this copy carries the print's dimensions -- likely a preview, not a production part') so the operator sees the preview at a glance before splitting or nesting. Nothing built until Jordan or Aristide says go. DESIGN PROPOSED 2026-09-15 (Jordan 1a: 'on one hand we want to clean the cad to reduce human labor inputs, on the other we want to show the operator as much of the source file as possible, without nagging or inducing unnecessary input'): annotation ink as its OWN silent display layer -- parse DIMENSION/TEXT display-only (never healed, never cut, never QC'd), draw dimmed beside the gray base layer with its own layer toggle (default ON, one key like the other layers); the part cluster owning the dimension ink gets a quiet 'likely a dimensioned preview' caption on canvas; the intake yellow's counts become a click that frames the ink. ZERO prompts, zero required input -- pure display, the cleaning pipeline untouched, so both goals hold: the cut geometry stays clean automatically AND the operator sees everything the customer drew. BUILT 2026-09-16 (Jordan 'Go on all'): engine annot.py (docstring = design record) -- whole-file scan reads ENTITIES-section TEXT/MTEXT (MTEXT formatting stripped, %%c/%%d/%%p decoded) and DIMENSION entities' anonymous *D block ink (LINE/ARC/CIRCLE/SOLID/LWPOLYLINE/POLYLINE/TEXT rendered as drawn, arrowhead SOLIDs as closed chains; a missing block falls back to a counted marker + the dimension's own text, never silent); DISPLAY ONLY by construction -- dxf_io/heal/QC/nest/post untouched, no number read here ever becomes geometry (pdf-dims-govern stays its own deliberate door). SERVER: run_pipeline persists geo.annot scaled by the SAME units statement and turned by the SAME intake rotation as the gray base layer (one frame, every layer; a display aid never breaks the pipeline -- failures log and serve empty); older geometry.json backfills on first GET from the job's RECORDED scale/rotation, cached once -- Jordan's CAMP parent shows its ink without a re-heal. UI: 'Annotation ink (display only)' joins the layer bar (key A, default ON, dimmed var(--dim), text drawn at its own height/rotation) -- the button appears only on jobs that carry ink; the ink's own footprint carries the quiet 'likely a dimensioned preview' caption (a caption, never a prompt). Proven: test_annot.py 15 NEW controls (block ink, stripping, missing-block fallback, part-geometry never in the ink, mm scale, rot90 exact quarter turn, backfill-and-cache) + app/verify_annot.js 8 live Playwright checks over the public URL (button/key/caption path, bare job shows nothing; fixtures removed). Honest limits: ink draws from the SOURCE frame (a moved/edited part leaves its ink where the customer drew it); MTEXT wraps and special dim styles render approximately -- display, never authority.
- •The hand editor on part-list nests -- drag, rotate, cross-sheet move, Alt-drag COPY (BUILT); true-nest snap-rewire retired by stage-4 door convergence record — Jordan, 2026-09-14, Go on all: the manual-nest-editor gestures land on the part-list Nest canvas — grab any placement and drag it anywhere, click a part and rotate/flip it freely (Illustrator shapes), drag a part from one sheet to another, and Alt/Option-drag COPIES it. ONE-CLICK ROTATE BUTTONS 2026-09-15 (Aristide go for next in his ratified order): +90 and 180 buttons join the hand-editor bar beside the typed angle -- the true-nest editor vocabulary (180 IS the nest flip, the flip-every-other-column form; a mirror is not a placement move); both drive the SAME draft-rotate arithmetic as the typed slot (one function, turn about the part own centre, draft until Apply, disabled without a selection). Client-only app.js, live on refresh; verify_plhrot.js 11 live real-mouse checks (compose 90+180, typed -270 parity back to zero, world centre fixed, other copies untouched, Apply + fresh-page persistence, disabled-without-selection) + verify_plhand.js 18 re-run green. TOOLPATH DOCK, BUILT 2026-09-19 (record; Jordan: MetaCam's dock 'magnets parts to a bounding-box boundary around the adjacent parts, with an easily overridable offset value. We could do one better, and use the toolpath rather than a bounding-box'; his 'Go'): the dragged copy magnets to the position where its TOOLPATH sits exactly the dock gap off a neighbour's toolpath -- true as-cut segs (tab gaps included) PLUS pierce and lead line, sampled per orientation from the pack's own plan rows (plDockSamples; old packs without segs honestly fall back to the true-bbox perimeter, exactly the MetaCam box dock this improves on). Sliding-profile arithmetic client-side (dockProfile/dockContact -- per-bin X/Y extremes, 0.25 bins widened one step so a bin-edge point never lands the dock closer than the gap; conservative, the judge on Apply still measures true outlines), so concave parts genuinely tuck into each other's reliefs (proven in the suite: a mover docks INTO a recess a bbox dock cannot enter). Gap per pair = the larger of the two parts' bridges (the recorded pairwise rule, read from report.bridges), sheet edge = the part's own bridge (the ruled per-part margin, exact); ONE typed field on the hand-editor bar overrides all pairs (isoinp = the Excel-entry seam, placeholder shows the bridge seed); Alt slides free, the house convention. Client-only app.js. NEW app/verify_pldock.js 14/14 (exact solver controls incl. the no-Y-dock-without-X-overlap null, live checks on the real 7497/7498 job draft-only -- Apply never clicked) + plhand 18/18, plhrot 11/11, plvariant 10/10, ctxmenu 26/26, partnav 15/15, pltabs 19/19 green. Honest limit: the TRUE-NEST hand editor still runs its 2026-09-05 box-envelope snap -- rewiring it onto this same contact solver is the stated next step, offered. RESOLVED 2026-09-19 (stage-4 door convergence, pin job-style-unification): the rewire RETIRES instead of building -- every new upload is a part list, so new jobs never reach the true-nest hand editor (the one road's pl hand editor carries the toolpath dock); old jobs keep the box-envelope snap as a legacy surface.
- •Illustrator-style isolation drill-down editing -- application-wide, full treatment (COMMISSIONED) ruling — Aristide, 2026-09-15 (nest screenshot of the big plate): grab a shape and move it with ALL its elements grouped; then, like Illustrator, double-click drills INTO the group -- edit one hole alone, drill into a letter A, then into the hole of the A itself. 'Make it clear in the interface that I am drilling down, just like in illustrator.' RECORDED LIMIT 2026-09-15 (found answering Jordan's 2a -- all-contours identity): the apply-to-all-identical twin test at the feature-with-children level matches the parent sig AND the children's SIG MULTISET but not the children's POSITIONS -- two bosses whose holes are the same shapes at different spots would falsely twin. LIMIT CLOSED 2026-09-16 (Jordan 'Go on all'): /isogroups now carries featsig -- chain + descendants grouped as one PART through split_parts.congruent_parts (the nest door's whole-part arithmetic: one rigid rotation + translation must map EVERY member contour, positions included), the cheap sig test kept only as a prefilter; the client's feature-level twin test reads featsig (sig-multiset stays the honest fallback on a pre-restart payload). Two features whose children merely share shapes at different spots never twin; a rotated true copy still does. Proven: test_isofeat.py 9 NEW controls (the old defect proven present via the prefilter, moved-hole never groups, rotated copy groups, read-only deterministic); isoserver 17 / isoident 12 / partgroups 21 green. Standing limit unchanged: mirrored copies group with their mirror (the per-chain sig's own recorded limit). GRANULAR SIZE EDITS BUILT 2026-09-18 (Jordan's drilled-circle screenshot -- 'the ability to, once I'm drilled down, to change the diameter of the circle ... if this were an arc ... change its radius. I'm missing the granular detail modification ability.'; his 'Go' -- also the tolerance-center ruling's promised manual hole-size lever, first built form): ENGINE heal_edit ops set_diameter (nearest TRUE circle -- closed all-arc chain on one center+radius sweeping a full turn; every piece scales radially about the untouched center so split points keep their bearings) and set_radius (the single arc under the click re-fit at the typed radius, ENDPOINTS HELD -- refuses under half the chord naming the smallest radius that fits; the new center keeps minor-minor/major-major, the bulge side being fixed by the direction of travel; a broken tangency is QC's honest flag, never hidden). Recorded heal edits exactly like move/rotate: replayed on every re-parse, removable per row, both fidelity exemptions on the report row, one Undo away, the standing stale rule fires downstream. CLIENT: right-click any healed arc -- a circle offers 'Set circle diameter (now O ...)', any other arc 'Set arc radius (now R ...)', prompt seeded with the current value, unit suffixes accepted, 4-decimal floor; the isolation bar grows a diameter field on a drilled TRUE circle (seeded live; Set commits through the identical-shape all-or-one modal, fanning the same diameter to every twin; the level re-drills through the radial map); Heal/QC panel rows name both edits in plain words. Tangent fillet resize (endpoints sliding along the neighbour walls to keep tangency) = the stated next step, offered. Verified: NEW test_setsize.py 27 controls (CW/CCW, minor/major sense, refusal words) + NEW app/verify_setsize.js 11/11 live on dev + neighbours iso 15/15, isoident 9/9 x3 (two earlier blips proven the suite's own isoReload race under load, baseline checked), ctxmenu 26/26, globalband 13/13, healqc 14/14, undo 17/17, gc56 25/25, test_healedit 78/noop 8/healadd 13; dev engine restarted.
- •grouped upload + the sorting tray -- the group is declared at upload, granularity on demand (SESSION 1 BUILT) ruling — Jordan, 2026-09-14: uploading several files should let the START of the upload say whether they become individual jobs or one grouped job (a part list). The split/add-all doors stay as break-in granularity; they are no longer how the flow starts. His bar on the tray design: 'make this the best damn interface you've ever designed. Think Apple ease of use and intuitive.'
ONE GROUP BY DEFAULT 2026-09-19 (Jordan: jobs with 25 or more files are common for him, so the common case should not need 25 drags -- his 'Go'): a multi-file drop now opens the tray with EVERY file already stacked into ONE group; the operator confirms and the whole batch lands as one part list. Two one-click reshapes on the tray: 'Separate all' unstacks to per-file cards (the previous default, for the drop that really is unrelated files) and 'Stack all' restores the single stack; the drag-to-stack granularity is unchanged once separated. verify_tray.js asserts the one-stack open, then Separates to prove the classic per-file flow still works; verify_multiupload.js separates before both confirms. Suites green same commit as the DWG intake.
- •Part-list display format -- 'hard for me to read' (Jordan 2026-09-15, awaiting Aristide) question — Jordan, 2026-09-15, on misreading his own 7497/7498 list ('My mistake, the list is hard for me to read. We will need to work on the display format to make things easier on the eyes. We will need Aristide's input on this.'): the Part list tab's row form needs a readability pass -- his misread was real evidence (he thought parts were missing that were listed). Nothing designed yet; waits for a sitting with Aristide (the ui-visibility-pass precedent: his items, our execution). Candidates when they sit: thumbnails per row (the jobs-home-filing ruling already says the shape is how the archive is browsed), stronger row separation, name-first columns.
- •Shop's one-sheet 7497/7498 nest MATCHED AND EXCEEDED: our pack lands all 14 parts on one sheet at less used X (2026-09-16) record — Jordan, 2026-09-15: 'we were able to get all of these parts onto one 60"x120" sheet on our end with manual nesting in MetaCam... See if we can do better, and I will send you our nest tomorrow morning.' His nest arrived 2026-09-16 (PDF, NC 33365): all 14 parts (7497 x9 + 7498 x5, qty 1 each) on ONE sheet, extent 119.564 x 58.585 measured off his own print. Ours had spilled 7497 parts 2+3 to a second sheet (banded packer, bbox density 95% -- only true-shape pockets close the gap; a probe proved ~305 legal pocket spots existed). The TRUE-POCKET SPILL RESCUE (pin pl-pocket-rescue, built same day on his 'I expect your nesting to match, or better yet, exceed what we humans can do') closes it: ONE sheet, 14 parts, used X 119.2614 -- 0.30 in less material than his hand nest, every always-apply rule on (ruled gaps on true outlines, lead/pierce envelopes reserved, audit +0.005 over ruled). The invariant derived, not the layout transplanted (nest-objective-function): his layout spends concave mouths between parts; the engine now sees them. The CAMP alternate-180 pairing (pin pl-pack-alternate-180) is a SEPARATE standing question -- untouched by this build, still awaiting its own go.
- •Congruent-outline runs pack as the MEASURED regular form -- the CAMP#4 staircase fixed (Jordan's go, 2026-09-16) record — BUILT 2026-09-16 on Jordan's go ('Go, and let me know when you're done building all'), from his CAMP#4 catch ('This nesting is illogical. It should be uniform alternating every other part flipped 180'). ENGINE part_list_nest._form_runs (the design record): parts whose OUTER outlines are congruent under translation (point-to-segment both directions at RUN_TOL 0.003 -- under the construction PAD so a twin's outline can never eat it; interiors free, the outline is what nests; an item without an ordered outer never groups -- conservative) pack through ONE shared proxy = the mini-nest common denominator (nest-alt-two-true-nests): the shared outline + the UNION of every member's lead envelope so any member sits in any slot, bridge = larger governs across members. The form is MEASURED, never assumed (_run_form): uniform(a) and alternating(a, a+180) columns are each simulated with the pack's own slide arithmetic, metric = real X per part over two columns (column widths AND inter-column spacing both land in the second column's top); a tie prefers the alternating pair only when the outline is 180-asymmetric -- a symmetric outline stays uniform, the flip would only flip interiors. The winner is FORCED: every column runs one fixed angle cycle (try_rows parity), so the lattice is regular by construction and the greedy staircase cannot form; a lone qty>=2 row with rotation freedom is its own run (regularity is for every same-outline stack, one row or many). Members return at _run_remap BEFORE the audit -- each placement takes its member's own id, bridge and frame delta, so the separate-arithmetic audit judges each member's OWN outline + leads at the ruled values (construction at the union/larger values holds a fortiori); filler extras remap to the run's filler members; the report carries runs [{members, qty, form, bridge}] and each placement names its run -- never silent. Unlike-outline parts keep the slide packer untouched. Proven: test_pl_alt.py 14 NEW controls (the staircase fixture -- same outline, two lead sets -> ONE run with strictly alternating columns and pair-period pitch; symmetric rectangle -> uniform; a 50-thou-different outline never twins; locked-rotation multi-member run uniform-0; frame-delta twin lands at the right world spot; larger-governs construction with per-member bridges recorded; filler member; mixed run + odd part) + 12 neighbour suites green. Jordan's CAMP#4 job lives on prod -- a re-pack from its Nest tab after the auto-deploy shows the regular lattice. Honest limits: congruence is translation-only in the shared part frame (a twin drawn ROTATED in its own file packs as its own run); the alternating pair meshes within the column -- the cross-column tuck channel (tuck_alt_flip) stays the true-nest engine's own.
- •INTERLOCKED run form in the part-list packer -- raised sub-columns measured as a candidate; CAMP#4 46.33 -> 46.15; MetaCam Algorithm 3 still wins at 42.65, verified legal, the oblique-lattice gap named (2026-09-16) record — BUILT 2026-09-16 on Jordan's 'Go', from his three MetaCam algorithm exports on the CAMP#4 kit (incoming/1789591653/55/57, Algorithm 3 = the winner, extent 42.65 x 35.05). ENGINE part_list_nest._run_form REWRITTEN: a form sequence is now [(angle, raise), ...] (position 0 always raise 0) and the candidates are uniform / alternating / INTERLOCKED -- the second cycle position raised d in real X so the 180 sub-column interpenetrates the 0 column, d SWEPT coarse (h/24) then refined (step/8), k-step cycles (2,3,4,6) with staircase raises; a skew family (same angle raised) rides per {a, a+180} class; ties prefer the simplest form (alternating < uniform < interlock < skew) and a 180-symmetric outline demotes to the same-angle name. try_rows unpacks (angle, dy); _pair_asymmetric REWRITTEN to a two-way congruence test (_haus) at RUN_TOL -- the old bin-profile test charged sampling scatter on slanted edges as asymmetry. Proven: test_pl_interlock.py 12 NEW controls (diamond checkerboard NEEDS the raise and beats any flat form; d in the name matches the measured level gap; chevron-with-flip proves flat alternating still wins when it should; leads/audit/regularity/mixed/spill) + test_pl_alt controls 2/3 updated (the L run now legitimately measures interlocked -- the deeper tuck; stale-verify precedent) + 12 neighbour suites green. THE REAL JOB re-packed via run_partnest under the job lock: 1 sheet, used_x 46.3284 -> 46.1469, all 15 runs interlocked 0/180 (sub-columns 1.80-1.92 apart), audit gap +0.005 / edge +0.005 over ruled, program dropped per the stale rule with O33342 kept. THE BENCHMARK VERIFIED BEFORE CONCEDING (the no-pleasing-shortcuts discipline, both directions): their export's 90 outlines are ONE congruent shape (perimeter 16.8525 on all 90, no interior holes) = our part 36 within 0.014 in mean after alignment -- like-for-like on part size (the prior session's 5.371 x 3.563 reading was a measurement error; theirs is 5.4621 x 3.8871 vs our 15 parts' 5.457-5.467 x 3.805-3.969); their tightest true-outline gap is 0.2883, LOOSER than our ruled 0.2, so the win is legal -- and a 0.288 channel holds our 0.13 lead with the pierce at standoff, so their layout is compatible with our tooling rules. APPLES-TO-APPLES: our packer on THEIR problem (90 copies of the one shape) measures 45.82 geometry span / 46.27 used_x vs their 42.75 -- the multi-shape kit is NOT the deficit. THE NAMED GAP (the honest concession, its own future build): their lattice is an OBLIQUE 2D form -- Y pitch 3.898 with the 180 sub-column at (+2.40, +2.61), lattice vectors not axis-aligned -- plus an irregular pocketed freeform tail on the right of the sheet (26 X-clusters, mixed orientations); ours is an axis-aligned column interlock with one raise parameter. The invariant to derive next: 2D lattice-vector search (both basis vectors free) per run, plus freeform pocket fill for the remainder -- not a transplant of their layout.
- •Rev2 offsets ARE the beam comps -- every filled Rev2 row packs/posts without a hand-typed materials.json duplicate record — Jordan 2026-09-16, on the CAMP#4 pack refusals (Mild Steel 0.040, then Stainless 0.125 -- both filled Rev2 rows with no materials.json backing): 'I'm lost. My Rev2 spreadsheet should have provided all the information you need' / 'It most certainly does have those beam comps.' RULING + BUILT same turn: the per-row calls he and Aristide already made for MSO7,0.375 (2026-08-31) and MSO7,0.135 ('Refer to the tables I provided', 2026-09-02) are now the STANDING RULE in tables.material() -- a key with no typed materials.json row derives kerf = beam_comp = the Rev2 row's offset (where the three class offsets differ, e.g. the Brass rows, the LARGEST governs: beam_comp is the interference gate and the biggest offset the control runs is what the gate must clear), lead length = the sheet's OWN Lead-in column (Stainless 0.125 runs its 0.125, not the derived 0.150 floor), provenance decided/Jordan/2026-09-16 riding every derived value. TYPED WINS field by field (seed rule): a complete typed row is untouched (MSO7,0.125 keeps its measured .007 over the sheet's .004), a partial row fills ONLY its gap (MSO7,0.060: typed kerf .007 stands, missing beam_comp fills .004 -- previously refused to post). Blank Rev2 cells stay TBD and refuse; an unknown key still refuses MaterialUnknown naming BOTH tables ('no row ... and no filled Rev2 sheet row for it'); the humanTableWords 'Material Not Defined' translation still matches the new wording. The MSO7<->Mild Steel post pairing rides (_REV2_CODE_GROUPS); one machine sheet today -- if a second machine's sheet ever carries a different offset for the same row, that becomes a per-machine materials question, recorded not guessed. Proven: test_rev2_material.py 13 NEW controls + neighbour suites green (matoptions 10 / etch 9 / rules / lead_tab_comp / tooling_doc / drag / nc_warn / matrecord / part_list / dragcheck / sketch / units_confirm / pl_post / pl_tool / pl_deliver / sheetsize). Engine change -- live on dev after this turn's restart; prod picks it up on the auto-deploy, then Jordan packs CAMP#4 with no new rows typed. Etch feedrates, asked in the same message: already recorded 2026-09-06 (etch_default #111 @ 100 in/min in machine_tables.json, tables.etch() serves it; a row's own etch columns win when the workbook grows them) -- the etch STEP consuming it is still its own future brick.
- •True-pocket spill rescue in the part-list packer (Jordan's 7497/7498 one-sheet benchmark beaten, 2026-09-16) record — part_list_nest.pack now runs _pocket_rescue after consolidation: the LAST sheet's parts move into TRUE 2D free pockets of earlier sheets -- concavities between placed outlines the banded full-height-column packer cannot see -- all-or-nothing per spill sheet, looping while whole sheets empty. Search: per rotation variant, x-then-y scan at 0.5 in step, leftmost first (slab-preserving); coarse pass = thinned points vs 1.0 in point grids at the smallest possible pair requirement (no false reject); exact pass per neighbour = cheap containment/conservative rejection FIRST (ray cast on ordered outer polys where known; missing outer = conservative bbox rejection, never nests inside a part), then full-resolution _mind at larger-governs + PAD, leads both directions at max(standoff, pair req). An interior cutout is NEVER a pocket (outer-contour containment test refuses it -- nest-in-hole stays the operator's option, pin nest-pocket-backfill). GATE CLOSED IN THE BUILD: distance-to-outline reads a lead point DEEP INSIDE a neighbour's metal as a large passing distance (the banded slide can never produce that -- contact stops at first touch; free 2D placement can) -- lead points now containment-tested both directions, and bbox-conservatively when no outer exists. Pocket placements carry pocket:true; used_x includes pocket extents past the column frontier (honest slab spend); moves recorded kind:pocket, never silent; the pack's own separate-arithmetic audit judges pocket placements like every other and raises on any miss. Outer outlines ride in from server._pl_build (split_parts.part_groups root outline per distinct part, None fallback = conservative). Proven: test_pl_pocket.py 10 NEW controls (mouth tuck, no-outer conservative, hole-is-not-a-pocket, all-or-nothing, two tucks clear each other at the ruled gap, audit, lead-envelope refusal with the bare part tucking, single-sheet no-op, rotation lock, used_x honesty) + 10 neighbour suites green (part_list 28 / pl_tool 18 / pl_consolidate 13 / pl_spread 32 / pl_post 26 / pl_seq 27 / plhand 25 / pl_deliver 27 / plvariant 23 / partrotate 19). THE REAL KIT: Jordan's 14-part 7497/7498 list (Mild Steel 0.125, GX 10K) packed ONE 120x60 sheet, used X 119.2614 vs his MetaCam hand nest's 119.564 (NC 33365) -- 7497 parts 2 and 3 tucked into true pockets at (43.205, 48.705) rot 0 and (70.705, 56.9834) rot 180, audit gap/lead/edge all over ruled. All rules on (no-rule-dropping-to-beat). Pack runtime ~80 s on the real kit -- the busy overlay covers it; speedup is future work if big lists stay slow.
- •Within-sheet FRONTIER TUCK in the part-list packer -- the last partial column moves into the sheet's own pockets (Jordan's 'I say build it', 2026-09-16) record — Born from Jordan's MetaCam three-algorithms screenshots (CAMP#4 outers-only, 36x60, 90 parts each): Algorithm 1's remainder trick -- the parts that do not fit the lattice tuck into frontier pockets instead of spending a full column advance of X. Named as the build candidate in the head-to-head thread; Jordan: 'I say build it.' ENGINE part_list_nest._frontier_tuck, called in pack() AFTER _pocket_rescue (rescued pockets are in the clouds the tuck must clear): for each sheet, the last column's parts each seek a true free pocket through the SAME _pkt_find arithmetic as the spill rescue -- one new gate, max_x = the vacated column's base (a used-X ceiling, not a sheet edge, so no bp margin against it), so the whole placed envelope (outline + leads) stays at or left of it and used X shrinks by the vacated advance. The remnant slab GROWS, never spends, so it runs automatically (the auto-never-spends-the-slab rule satisfied, not excepted). All-or-nothing per column with rollback (a partial move never strands the frontier); members try big-first so a full-rhythm column's hardest part fails its one ceiling scan and the attempt aborts cheap -- no count heuristic (on a mixed list a small-part column legitimately holds more parts than a big-part one); cascades while a frontier empties; a pocket already past the new frontier = no gain, skip. Moves recorded kind 'frontier' with the ceiling named, never silent; the one final _audit judges tucked placements exactly like every other. Proven: test_pl_frontier.py 8 NEW controls (tuck into C-mouths shrinks used_x with the ceiling respected on every tucked envelope, all-or-nothing rollback at five-parts-four-mouths, full-rhythm same-part column stands byte-identical, single-column sheet untouched, filler flag + asked/filled survive, lead envelopes ride the tuck with audit lead minima non-negative, bounded _pkt_find proven both ways) + 11 neighbour suites green (part_list 28 / pl_pocket 10 / pl_consolidate 13 / pl_spread 32 / pl_tool 18 / pl_post 26 / pl_seq 27 / plhand 25 / pl_deliver 27 / plvariant 23 / partrotate 19). Real CAMP#4 90-part job re-packed via the server codepath: layout byte-identical at used X 46.3284 -- its alternate-180 columns end EVEN, no remainder to tuck (the honest no-op regression; 66 s pack, tuck cost negligible). The tuck pays when a remainder exists -- the Algorithm 1 case (84 lattice + 6 remainder) and any ragged qty.
- •Tab unification -- every job kind carries the ONE tab strip (stage 1 BUILT 2026-09-16); stage 2 COMPLETE 2026-09-20 -- the selected part rides the tabs: Geometry/Tooling tab clicks OPEN it, sibling strip on the part's drawer, converged sheet-tab clicks land on the list; NOTHING VANISHES 2026-09-20 -- a group member part carries Part list as its FIRST tab, the back chip retired recordmixed — Jordan + Aristide sitting together, 2026-09-16. Aristide's observation on the part-list screen: 'the screen we're looking at is in a "sub-program" with no clear path (crumb trail) back to the main interface' -- correct, and by a design the product outgrew: brick 21 ruled exactly two tabs (Part list + Nest) when the list had nothing else, then posting (29), delivery (30), sequencing (31), spread, the hand editor all landed INSIDE the Nest drawer -- a whole sub-application with a different vocabulary, which is also why Jordan could not find his posted NC. Jordan's ruling ('In my mind, I would think Part List would be the first tab, and we can treat each job the same. If we only have one part, no problem. If we have many parts, also no problem.' -- his Go on the staged plan): one tab strip on every job kind. STAGE 1 BUILT same day (record): a part-list job carries Part list / Geometry / Quote / Tooling / Nest / Post / Deliver -- the sheet programs re-homed from the Nest drawer onto the POST tab (verbatim rows: O-numbers, warnings, downloads, post/re-post), the versioned delivery chain onto the DELIVER tab; the Nest drawer keeps pack/spread/part-order/hand-editor and points to the new homes; Geometry/Quote/Tooling render honest not-wired notes (brick-1 pattern) with NO false pending dots (stagesFor returns empty for them); Post carries a REAL stage record now (run_plpost writes stages.plpost done/flagged, _drop_plpost pops it -- a dropped program never keeps serving a dot; pre-record jobs derive the dot from the living post_doc, never lying pending); deep link #tab=post lands uniformly; canvas shows the sheet scene on Post/Deliver view-only (interactions stay guarded to the Nest tab); topbar names the kind ('Part list -- <name>') and a member job carries an 'in group <name>' crumb chip back to its list (groupChip). Proven: test_pl_post 26 / test_pl_deliver 27 / test_pl_seq 27 green + app/verify_pltabs.js 18 NEW live Playwright checks over the public URL (real 7497/7498 list, single-part regression; the hash-only SPA lesson re-hit in the verify and fixed with fresh pages). STAGE 2 (intent): row-select drives Geometry/Tooling in place -- the list job carries a current-part selection, a picked row renders that part's own record through today's renderers, edits land on the part's own job (one truth, stale rules unchanged). STAGE 3 (intent): one nest door -- the list shape picks the pipeline (one distinct part = true nest with macros, several = part-by-part), recorded ran+source, operator-overridable; the engines converge piece by piece, each merge its own proven brick. Single-part jobs gaining their own one-row Part list tab rides stage 2's design. Related: jobs-home-filing, ui-partlist-brick21, grouped-upload-tray. RETURN CHIP ON EVERY PART-OPEN DOOR + BROWSER BACK/FORWARD AS REAL NAVIGATION, BUILT 2026-09-19 (record; Jordan's live session on the 7497/7498 list -- he opened a part from the canvas right-click menu and had no marked way back, and browser Back left screen and address out of step; his 'Go'): (1) every canvas door that opens a part sets the SAME return chip the Part-list row door does -- the ctx-menu 'Open part detail' and the brick-34 deferred single-click door both route through plOpenPlacement -> KM.openPart with placement context (sheet/index/item/copies over the saved layout, so the isolation variant fork keeps working); the hand-editor drill already did. (2) the hash names the screen (#job=id&tab=k): opening a DIFFERENT job pushes ONE history entry, tab switches and same-job rerenders replace in place (navHash), leaving a job for home pushes; a popstate handler re-runs the hash router, and a Back landing on the stashed part list rides KM.plBack so unsaved draft rows survive the trip; a tab in the hash is honored only when the hash addresses the job being opened, so another job's tab never leaks into a fresh open; our own pushState/replaceState never fire popstate -- no loops. Client-only (app.js). NEW app/verify_partnav.js 15/15 on the real 7497/7498 list (read-only, every door a GET) + pltabs 19/19, ctxmenu 26/26, refusal 16/16, undo 17/17 (fresh scratch) green. STAGE 2 RESHAPED 2026-09-20 (Jordan: 'it would feel natural for the operator to carry that list over to the Geometry tab, and be able to open each from there'; his 'Go'): the Geometry tab of a part list carries the SAME preview-card list the Part list tab renders (one component, never a fork) -- a part list still has no geometry of its own, so opening a row from the Geometry tab lands on that part's own Geometry screen (the open button rides the hash, one history entry, the back chip returns to the list); the earlier 'edit the selected row's part in place' stage-2 wording is superseded by this list-carry shape on his word. SELECTED PART RIDES THE TABS 2026-09-20 (Jordan: "wouldn't it be easier for the operator if when we have the selected part from the Part List up on the canvas, when we go from tab to tab it's already 'open' for them to do modifications?", his 'Go' -- plus his Tooling-tab catch 'Tooling tab already has a nest... I was under the impression that it wouldn't nest until we get to the Nest tab'): the open click is GONE -- on a part list, clicking the Geometry or Tooling tab OPENS the selected part's own job on that screen (KM.openPart rides the tab click, back chip set, edits still auto-update the nest on return); the listed parts ride the part's Geometry/Tooling drawer as a thumbnail SIBLING STRIP (plSibStrip, stash road only -- one click hops parts on the same tab, shared-prefix tail rule via the shared plSharedPrefix helper, unsaved draft rows survive every hop; a part opened cold shows no strip, the group crumb stays the way in); the mirror move -- a converged member's Nest/Post/Deliver tab CLICK goes straight to the list's matching tab (plBack grew a tab landing with the same fit + auto-nest-update rules a tab click runs; the plgoto pointer stays for deep links); the sheet stops drawing on the list's Tooling tab (plPreviewOn grew tooling -- Jordan's catch was the preview-cards gate covering only Part list + Geometry), the Tooling fallback drawer carries the same card list, and the stale 'open the referenced part jobs' wordings on the Heal/QC and Dimension panels now say the new road. Supersedes the open-button-as-the-road form of 2026-09-20 morning on his word; the open buttons stay as working shortcuts. Client-only (app.js/app.css), zero server change. Proven: verify_pltabs REWORKED to the new design 19 -> 24 (tab click lands on the part + chip + strip; the honest notes moved to the deep-link fallback checks) + verify_converge 17 -> 18 (member Post tab CLICK lands on the list's Post) + plreturn 11, partnav 15, plpick 17, fork 24 (dev) + plrefresh 18, refusal 16, oneroad 17, undo 17 (fresh scratch) + settled live walks on the real 7497/7498 list (redirect, 14-sib strip, sibling hop, part Tooling native) and a scratch one-road job (chip 'back to cvsolo', Post click riding plBack onto the list's Post tab, honest pack-first drawer).
NOTHING VANISHES (2026-09-20, Jordan's Geometry_9-20-2 catch -- 'when I select a part from the Part List, and click the Geometry tab, then the Part List is no longer a tab? It's honestly a bit confusing', his 'Go'): a part living on a list keeps the LIST'S OWN STRIP -- tabsFor prepends a Part list tab (first, the list job's own position) on any group-bound member and on any part opened from a list (the plReturn stash road), so the strip reads identically on the list and on the part. The tab click is the way home: the stash road rides KM.plBack('partlist') so unsaved draft rows survive, a cold-opened member opens its group's list plain (one pushState entry). The BACK CHIP RETIRED -- plChip now only removes the element; the tab says the same thing in the same row as every other screen (the stash + plBack machinery stay, the chip was a second voice). stagesFor gives the member's Part list tab NO stages (pure navigation, no dot -- Jobs-home cards unchanged); deep links naming #tab=partlist on a member fall back honestly (openJob + popstate guards). The group crumb and the sibling strip stay. Client-only. Proven: verify_pltabs REWORKED to the design 24/24 (chip checks became first-tab checks; the single-part regression now asserts Part list first exactly when the job is group-bound) + verify_partnav REWORKED 15/15 (both part-open doors assert the leading tab, not the chip) + live walk on a scratch kit list (stash road, cold member open, round trips). verify_plreturn + verify_plvariant still assert the retired chip -- QUEUED for the same design rework, stated honestly.
- •Global controls -- band select, delete, context menu, undo/revert, copy, aim feedback are APPLICATION-WIDE (Jordan 2026-09-16); items 1+2+3+5+6 BUILT 2026-09-18, undo/revert landed under pin global-undo rulingmixed — Born from Jordan's Geometry-tab ask (rubber-band delete a dimensioned preview part): band select existed but was parked inside the Heal/QC panel's armed mode. His ruling: 'These types of functionality should not be limited to one panel, these are global controls that I need.' AGREED GLOBAL SET (his 'agree with all' 2026-09-16): (1) shift-drag band select on every canvas, selecting what the screen shows -- geometry segments on Geometry, features on Tooling, placements on Nest; (2) Delete/Backspace acts on the selection globally (Geometry: recorded delete edits via the standing /healedits door; nest draft: remove placements; Tooling: honest 'nothing to delete'); (3) right-click context menu on anything anywhere (the per-contour right-click ruling generalized); (5) Alt-drag copy extended to the true-nest hand editor (one copy gesture app-wide, the pl-nest form is the reference); (6) the dimension tool's magnet/aim feedback (snap marker + hint chip) extended to every armed click -- Join, Fit Arc, drag-end-to-point, add-feature pick. UNDO/REVERT MODEL (his Illustrator counter -- ~100 undos + revert -- answered with the per-JOB shape; points 5 and 6 AGREED 2026-09-16, point 4 clarified same day: undo of a geometry change DOES affect downstream -- it IS a geometry change, so the standing stale rule fires, downstream judgments drop and re-run, O-numbers kept, delivered files untouched, the toast says so in plain words; non-geometry undos such as a dimension label move touch nothing downstream): one chronological journal PER JOB of every recorded change across all tabs (heal edits, override sets, dim moves, nest layout saves, sequence clicks); Ctrl+Z anywhere undoes the job's last change with a toast naming what+where and a jump link; redo Ctrl+Shift+Z on the same stack; depth ~200, journal persists on the job record (survives reload); REVERT with two anchors -- 'back to fresh upload' (drop every recorded change, pure automated heal) and 'back to delivery vN' (the job-versioning chain is the save history; restores the working state a delivered version shipped with, delivered files immutable forever); NEVER in the stack: deliveries, assigned program numbers, shop tables and rates, customer filing. THE OPERATOR NOTE (Jordan's explicit ask, this pin is its home): no operator accounts exist yet, so two stations working the same job share ONE undo history -- last change is last change, whoever made it. Jordan: 'We'll deal with the operators once we add that as a feature.' When operator accounts land, the undo journal gains per-operator attribution (who made each change, whose change an undo takes back) -- fold that into the operator-accounts build, and this pin carries the context so it is not re-litigated. STATUS: discussion phase ('Let's discuss before you build') -- nothing built; proposed build order awaiting his word: items 1+2 first (his immediate need), then 3, then journal + Ctrl+Z/redo, then revert, then 5+6. ITEMS 1+2 BUILT 2026-09-18 (Aristide '1. Go' on the staged build, docs/staged-2026-09-16-global-controls-build.md): shift-drag band select needs NO armed mode -- on the Geometry scene the gesture starts the armed bandsel's OWN band state (one machinery, never a fork; guards: geometry tab showing healed prims, never a part list, never the sketch preview, never with a Heal/QC or Dimension mode armed -- an armed pick keeps its own gesture; plain drag stays pan, shift is the discriminator), release/draw/Delete/Escape paths accept 'selection exists' instead of 'mode armed' (Delete tab-guarded to the geometry scene so it never acts on a selection the screen is not showing); Tooling and nest-draft shift-bands already global, unchanged. Delete/Backspace global: Geometry = recorded delete_seg rows through the standing /healedits door (one save, per-row removable, engine refusals verbatim); nest hand draft = trims the draft (already live); Tooling = honest no-op toast in plain words ('Nothing to delete here -- Tooling features are the judged cut geometry'). The panel button still arms the mode for discoverability; armed behavior byte-identical. NOTE the undo model here is superseded on scope by pin global-undo (Aristide 2026-09-18: TRUE GLOBAL, one stack crossing jobs, navigate-to-the-spot per step) -- the journal design lands there. Item 3 (context menu) queued next; items 5/6 later sessions per the ruling. ITEM 3 BUILT 2026-09-18 (Aristide 'Go for 1' on the offered queue): RIGHT-CLICK CONTEXT MENU everywhere -- the per-contour right-click ruling generalized. One floating menu; every item calls the SAME function its drawer/panel button calls (one machinery, never a fork), so the menu can never drift from the drawer; the browser's own menu is suppressed ONLY when we have something to show -- empty space keeps it. Scenes: TOOLING/POST features (right-click selects like a left-click but PRESERVES a multi-selection containing the hit, so a banded word still marks as text whole; items: 'Start / pierce here' applies the clicked point through the standard ovApply channel + re-plan, start-anchor corner pick, start-default modal, focus-the-drawer-input items for lead length / tab size / condition class, force/clear comp, treat-as-text, apply-to-all-identical toggle when the engine's record names twins; the Post tab's menu is the one-item door back to Tooling), GEOMETRY healed segments (recorded delete_seg / join / fit_arc heal edits at the clicked point through the standing /healedits door, 'Delete N selected segments' on a live band, remove-dimension on a dim label; an armed mode keeps its click -- the menu offers only the way out, the Escape ladder's own clearing), TRUE-NEST placements (enter the hand editor / click-in-order from the saved layout; inside the editor rotate 90/180, add copy, delete, and group/split/delete on a band selection), PART-LIST placements (open the part's own job, enter the hand editor; in hand mode rotate 90/180, duplicate, remove -- all the bar's own functions). A right-button press never starts a canvas gesture any more (grabs and pans are left-hand; the press belongs to the menu). Armed picks (start pick, corner pick, click-in-order) answer with a single cancel item. Client-only (app/web/app.js + app.css), serves on refresh. Items 5/6 (Alt-drag copy in the true-nest editor; magnet/aim feedback on every armed click) stay queued per the ruling. ITEMS 5+6 BUILT 2026-09-18 (Aristide 'Global control items 5+6'): (5) ALT-DRAG COPY in the true-nest hand editor -- the part-list hand editor's gesture is the reference form: Alt at the GRAB clones the placement first, the copy is its OWN entity (new group id, like Add copy) and rides the cursor alone whatever the move-mode pick says; an Alt-click that never moved keeps its copy in place, and the grab-time snapshot is KEPT so one Undo removes a copy either way; plain grabs byte-identical. GESTURE MOVED, recorded: Alt at the grab used to mean 'drag free, no snap' -- that form is now Alt pressed AFTER the grab (Alt during the move still suppresses the tooling-envelope snap, so a held Alt-copy drags free until released); verify_groupsnap updated to the surviving form. (6) the dimension tool's magnet/aim feedback (snap ring + hint chip, ONE shared chip renderer aimChip so the two surfaces can never drift) extended to every armed Heal/QC click: Join aims at the nearest segment END, Delete and Fit Arc at the nearest point ON the curve the click will address, drag-end-to-point rings the grabbable loose end within the grab's own reach (warns when none; the live end-drag carries the chip too, naming where the drop lands -- its own carrier or the snapped base point), the add-feature pick carries the full corner/centre/edge magnet. Same contract as the dim magnet -- a PICKING aid only, the raw click still goes to the engine whose own nearest-entity arithmetic stays the authority -- EXCEPT the add pick, where no engine snap exists: there the magnet FILLS the typed position fields (what the ring shows is what lands), typed stays the record, Alt places at the raw click. Empty-reach chips warn in plain words; band select with no loose end in reach shows nothing (the band is the gesture). Client-only (app/web/app.js), serves on refresh. Verified: NEW app/verify_gc56.js 25/25 on scratch; neighbours green same run -- nestedit, groupsnap 9/9 (suite gesture update), stampdel, handsafe, healadd 12, bandsel, iso 15/15, undo 17/17 (fresh scratch), globalband 13/13, bandselgeo 13/13, snapdrag 10/10, dimdrag 10/10, dimaim 12/12, dimarm 12/12, ctxmenu 26/26, plhand 18/18. Pre-existing stale, NOT this build (fail identically on the stashed baseline): verify_nestedit2 (free-angle Set + macro-sub expectation, checks 4/10) and verify_groups (drawer serpentine-select timeout) -- queued to unstale. The ruled global set is now fully built; the undo/revert model lives under pin global-undo.
- •NESTING STRATEGY SELECTOR -- candidate algorithms judged coarse on the outer toolpath, the measured winner full-packs; every candidate's numbers beside the winner (Jordan's 'Go' 2026-09-16 session 1; session 2 Aristide '6' 2026-09-18: freeform pocket fill; session 3 Aristide '8' 2026-09-18: oblique lattice; session 4 Jordan's beat-my-nest 'Go' 2026-09-20: DEEP FILL -- five candidates) record — BUILT 2026-09-16 (session 1) on Jordan's numbered go ('1. I agree, we need to judge the outer toolpath, not the geometry. 2. I trust your judgement. 3. Perfect. 4. Go.') -- his framing: MetaCam offers three algorithms and the operator trial-and-errors them; ours runs every candidate itself and picks by the measured number, keeping MetaCam's versatility while dropping the labor. ENGINE (part_list_nest): pack() gains strategy= ('runs' = the standing default, congruent runs as their measured regular form; 'mixed' = greedy mixed columns, no run forcing -- the pre-run form, can win material on some kits at the cost of the regular pattern; unknown strategy refuses in honest words) and coarse= (rank-only judge mode: the final separate-arithmetic audit is SKIPPED and the report says coarse -- the layout is never shipped). select_pack() = the shell: every candidate packs COARSE (_coarse_items: fresh dicts, outlines thinned every 2nd point above an 80-point floor -- chord sag stays under RUN_TOL so coarse run grouping matches full, proven by control 11; LEAD/PIERCE points kept WHOLE per Jordan's ruling -- the judge ranks the outer TOOLPATH, never bare geometry), rank = (sheets, total used X, -largest clean free pocket -- the third key added 2026-09-20, pin nest-free-area-product) with FULL ties keeping candidate order so the regular pattern wins a tie (the consistency objective); the WINNER then packs at FULL resolution with every rule and the refusing audit -- a coarse approximation can never ship a layout, only pick which algorithm ships one. report.strategies carries every candidate with its numbers (estimate:true on coarse losers; the winner's numbers are the full pack's own), report.strategy_source names measured vs operator; a candidate that refuses records its words, all-refuse re-raises the first (engine words). SERVER run_partnest: body.strategy = the operator's forced pick for THIS pack (plain-400 on junk naming the candidates; a plain re-pack re-judges -- stated on-screen). UI: 'nesting algorithm' block in the part-list Nest drawer -- per-candidate rows with sheets/used-X and estimated-vs-measured labels, CHOSEN marker, 'pack with this one' button per rejected candidate (aid-the-operator: the pick is one click, recorded source operator). Proven: test_pl_select.py 12 NEW controls (winner report byte-equal to a direct full pack of that strategy, force path, refusal ladder verbatim, coarse thinning honesty incl. lead points whole + run congruence preserved, tie order, all-refuse) + the real CAMP kit probed offline through the server's own _pl_build (no writes -- the live layout and O33342 stand). Stale test_pl_alt control 9 updated same commit (the locked L legitimately measures interlocked since the interlock build). NEXT SESSIONS (the go covers them): the oblique 2D lattice-vector search and the freeform pocket fill join STRATEGIES as candidates -- the Algorithm 3 gap; deeper coarse tuning measured, not assumed. SESSION 2 BUILT 2026-09-18 (Aristide '6' on the standing queue): the FREEFORM POCKET FILL candidate joins STRATEGIES ('free', deliberately LAST so a regular pattern wins any tie) -- every copy drops as a free 2D first-fit placement, leftmost-then-lowest-then-rotations, through the SAME _pkt_find clearance arithmetic the spill rescue and frontier tuck already run (larger-governs bridges on true outlines, lead envelopes both directions at max(standoff, gap), containment tested -- never nests in a hole, that stays the operator's option per pin nest-pocket-backfill); no columns, no runs, so it can win material on irregular kits at the cost of BOTH regular-pattern objectives (breakout consistency, one-value-editable G-code) -- which is why it only ever ships by measurement or the operator's pick. Its filler pass fills open sheets only (never a new sheet); the spill rescue and frontier tuck are skipped by construction, not exception (first-fit already tried every earlier sheet with the same arithmetic and sheets only ever fill, so a rescue scan can never find a spot the placement pass just rejected; the tuck needs columns); used_x honestly reports the widest placed envelope per sheet. Server door and drawer block needed ZERO change -- both are driven by STRATEGIES / report.strategies, the one-machinery rule paying out. Proven: test_pl_select.py 12 -> 16 controls (three candidates in order, free full pack under the separate-arithmetic audit, the ring-and-plug never-in-hole control, force='free' with the others honestly skipped, refusal words name all three) + 13 neighbour suites green (part_list 28 / pl_alt 14 / pl_interlock 12 / pl_pocket 10 / pl_frontier 8 / pl_consolidate 13 / pl_spread 32 / pl_tool 18 / pl_post 26 / pl_seq 27 / pl_sync 23 / plvariant 23 / partrotate 19); the REAL CAMP kit probed offline through the server's own _pl_build, NO writes (the live layout and O33342 stand): runs 1 sheet 44.69 used-X CHOSEN full, free 58.16 estimated, mixed 59.60 estimated -- free honestly beats mixed on this kit and loses to the regular pattern, and the judge now says so in numbers instead of anyone guessing; the third candidate adds ~28 s of coarse judge time on the 90-part kit (stated, never hidden). The session-1 rider landed: NEW app/verify_plsel.js 17/17 LIVE on scratch 8479 (three candidate rows with estimated-vs-measured labels + CHOSEN marker, one-click 'pack with this one' forces free recorded source operator with the skipped candidates saying so on-screen, plain Pack again re-judges back to measurement) + verify_refusal 16/16 + verify_pltabs 18/18. Honest limits: the oblique 2D lattice-vector search stays the remaining candidate (session 3 -- a genuinely different construction, the true-nest double-lattice search generalized to multi-part kits, never a transplant); free's x/y scan cost grows with sheet count, so the shared coarse-scan speedup rides that session too. SESSION 3 BUILT 2026-09-18 (Aristide '8' on the standing queue): the OBLIQUE LATTICE candidate joins STRATEGIES ('lattice', SECOND in candidate order -- it is a regular pattern, so it outranks mixed/free on ties) -- trial_lattice.py's true-nest double-lattice search GENERALIZED to part-list kits, a real generalization never a transplant. It forms the SAME congruent-outline runs as 'runs', then _lattice_form() MEASURES each run's lattice: the cell is one allowed angle or an a/a+180 pair slid to contact; Y pitch py by contact over ALL interacting multiples; column advance vx by contact for each of 8 swept oblique vy values -- every accepted offset proven exact on FULL sampled lead/pierce envelopes (_lat_ok probes the same _pkt grids; _lat_contact scans down then bisects so contact can never tunnel; containment both ways, never-in-hole stands). Cells tile big-run-first as full-height columns, column j at Y-phase (j*vy) mod py, so a verified clearance is inherited by its whole lattice residue class; runless parts, leftovers and fillers drop through the SHARED _pkt_find machinery (fronts per sheet); a kit with NO congruent run refuses in honest words and the selector records the refusal while shipping another winner. Score = material per cell (py*vx/ncell), straight advance wins ties. RIDING IT, the shared coarse-scan speedup for EVERY strategy: _pkt_pvars identity-keyed envelope/thinned-set cache + a per-sheet-data no-fit memo in _pkt_find (sound: a sheet's constraint set only ever grows, rollbacks discard whole data) -- multi-sheet free pack 8.2s -> 3.5s (2.3x), placements proven BYTE-EQUAL warm vs cold, cache invalidates on geometry swap. Server door + drawer: again ZERO change (STRATEGIES/report.strategies drive both). Proven: test_pl_select 16 -> 21 controls (full lattice pack under the separate-arithmetic audit; the HEXAGON kit where lattice STRICTLY beats all three -- 7.38 used-X vs runs 7.81 / free 7.71 / mixed 8.41, advance (3.178, 1.835) = the genuinely oblique close-pack with vy = py/2; no-run refusal recorded + another winner ships; the locked-lone-part honest limit -- cross-part skew gains live in runs' interlock, control 20 pins it; speedup byte-equality + geometry-swap invalidation) + the same 13 neighbour suites green; REAL CAMP kit probed offline via _pl_build NO writes: runs 44.69 used-X stays CHOSEN, lattice 55.91 est honestly beats free 58.16 / mixed 59.60 and loses to runs' interlock on this kit -- the judge says it in numbers; lattice adds ~72 s of coarse judge time on the 90-part kit (stated, never hidden). app/verify_plsel.js updated to FOUR candidates: 17/17 live on scratch 8479 + verify_refusal 16/16 + verify_pltabs 18/18 (both live dev, dev O-numbers consumed). Refusal words name all four candidates. The candidate roster from Jordan's MetaCam framing is now COMPLETE: runs, lattice, mixed, free. DEEP FILL joins STRATEGIES 2026-09-20 (session 4; born from Jordan's hand move on his re-packed 26342 nest -- he dragged a column square into the bumper's centre notch: 'Look what I did in a couple of seconds, using my human mind. We want to exceed human nesting potential, right?'; his ruling recalling the selector's own promise: 'you said that we could have many nesting algorithms to meet many scenarios, and beat human nesting capabilities with the added bonus of selecting the best algorithm for the job at hand... I expect you to beat my nest... Go'): strategy 'deep', fourth in candidate order (regular patterns still outrank it on ties; free stays last). The design closes the exact gap his move exposed: the pocket-drop pass (pin nest-free-area-product) judges PER PART and never touches a frozen column, so his sheet-level move was refused BY DESIGN, not missed. _deep_fill runs as the strategy's own pass (coarse judge included -- the pass IS the algorithm, so its estimate carries it): EVERY placement may move, columns may break; each part is offered the lowest legal spot via the same _pkt_find_low clearance arithmetic (max_x = the sheet's used X, so used X never grows); acceptance is judged at SHEET level -- used X shrinks, or the measured clean free rectangle grows, or the plateau clauses (falls at least 0.25 with the pocket held, or pulls left at least 0.25 without rising, pocket held) that unlock joint gains no single move can show. Termination: gravity strictly lowers an envelope top and a left-pull never raises one, cap 256; moves kind 'deepfill', notes carry used-X + a0 -> a1 pocket numbers and say 'columns may break'. Measured on the saddle-kit control: runs leaves the frozen 2+2 column standing, ZERO moves, pocket 1281.2; deep makes 4 deepfill moves, both squares land inside the saddles' notches, pocket 1436.2, used X equal 51.065 -- and the selector CHOOSES deep on measured numbers (runs 1286.6 / lattice 1391.8 / mixed 1281.2 / deep 1436.2). On the BG synthetic repro deep TIES runs exactly (2296.1 both) and runs keeps the full tie -- the regular pattern wins, as ruled. Deep subsumes the pocket drop (same 1436.2 on the stray control). Honest costs: select_pack 141.5s -> 198.2s on the BG kit (the deep coarse candidate, stated never hidden). Honest limits: the selector's estimates decide which algorithm full-packs (the recorded selector design -- a kit whose deep gain only shows in the full pack keeps the regular winner); Jordan's own prod hand layout is unreachable from dev, so the beat is proven on the synthetic control, his 26342 job judges deep on its next Pack again. Proven: test_pl_free.py 31 -> 42 controls (runs-frozen zero moves, deep breaks the column into both notches, pocket beat > +100 sq in, used X equal, audit passes the moved sheet, 5 candidates, deep CHOSEN, refusal words name deep) + test_pl_select 21 (roster edits only) + part_list 28, pl_macro 25, pl_sync 23 zero edits + verify_plsel.js FIVE candidates live on scratch + plrefresh, refusal, pltabs, plhand. Zero dev O-numbers (packs never post). JUDGE CHANGE 2026-09-21 (Jordan's 26342 Fill catch, recorded on pin part-list-nest-columns): coarse judge packs NEVER fill -- the selector ranks every candidate on the ASKED layout; filler extras ride only the winner's full pack, after consolidation and the rescue. JUDGE NOTE 2026-09-21 (the Nest_9-21-3 missing-parts fix, full record on pin nest-free-area-product): coarse judge packs run UNREFINED (refine=False -- no settle-to-contact, no column-continuation) by the selector's own design -- estimates rank the candidates and only the winner's full pack pays true-contact arithmetic; refining the discarded candidates measured ~3x judge time and was gated out.
- •FREE AREA IS A PRODUCT -- the packer squares up strays, measures every sheet's clean free area (slab + largest on-sheet pocket), and the strategy judge ranks by it on a used-X tie (Jordan's BG-BUMPER ruling 2026-09-20: 'the large empty area in the upper right of the nest could be used for other jobs of the same material ... it would be both logical and efficient to maximize that space'; his 'I want you to make all 3') recordmixed — BUILT 2026-09-20, all three pieces of the offered brick. (1) SQUARE-UP pass in part_list_nest.pack (after rescue/tuck, before _run_remap): stray placements -- any placement NOT in a same-part+rotation left-edge column of two or more -- are pulled left through the SAME _pkt_find clearance arithmetic every pocket move uses, capped at their own envelope's right edge so used X never grows; columns and arrays never move (the consistency objective stands), lattice moves only its pocket drops, and strategy 'free' SKIPS the pass by construction (measured on the 36-pc kit: 0.15 in of gain for 39 s of judge time -- the free packer's drops are already left-greedy; honest limit stated in code). Moves recorded kind 'squareup', one placement per pass, cap 256. (2) MEASURED FREE REPORT: report.free per sheet = the full-height remnant slab (sheet width minus used X, measured from placed ENVELOPES via _wbox -- row bookkeeping overstated after square-up) plus the largest clean rectangle inside the used area (classic left-edge sweep over envelope boxes, floor 1 sq in); used_x itself now measures placed envelopes. (3) THE JUDGE'S THIRD KEY: select_pack ranks (sheets, total used X, -largest free pocket) -- on a used-X tie the candidate leaving the biggest clean pocket wins instead of list order; full ties keep candidate order (consistency narrowed, not retired); every candidate row carries free_pocket beside its numbers, the forced-pick path too. STALENESS HONEST: run_plspread and run_plhand pop report.free before saving (spread moves Y, hand moves anything -- measured pockets would lie); plrefresh keeps placements so free stands (tiny envelope drift accepted, stated). CLIENT: Nest drawer states per sheet the slab WxH and the clean pocket WxH (area) 'usable by another job of this material'; candidate rows carry 'clean pocket N sq in'; judge note names the third key. MEASURED on the BG-BUMPER synthetic repro (one 76-in pinned bumper + 15 small): runs/mixed/free all tie used_x 76.355 -- the old judge picked by list order (a coin toss dressed as a judgment, stated to Jordan); now runs/mixed measure 2667.4 vs free 2161.2 and the pick is a real decision; full pack reports slab 43.64x60 plus pocket 58.15x39.59 = 2302.2 sq in. PROVEN: NEW test_pl_free.py 23 controls (rect arithmetic, BG kit square-up + audit, pocket genuinely empty, columns never move, judge third key end-to-end, forced pick) + test_part_list 28, test_pl_select 21, test_pl_macro 25, test_pl_sync 23 (zero suite edits) + verify_plsel.js 17->20 on scratch. Zero dev O-numbers (packs never post). POCKET DROP added same day (session b; Jordan's circled small parts on the 26342 nest -- 'I circled a couple of small parts that could easily fit between the two large parts', his 'Yes' on the offered build): a fourth piece of the same brick. _pocket_drop runs after the square-up on FULL packs only (the coarse judge ranks the algorithms' own forms, every non-free candidate skips it equally): every stray is offered the LOWEST legal spot on its own sheet via _pkt_find_low (the same _pkt_ok clearance arithmetic scanned y-major at 2x the grid step, its own nofit key; max_x = the sheet's used X so used X can never grow), and the move is accepted when the part lands strictly lower AND the measured clean free rectangle does not shrink -- GRAVITY WITH THE POCKET HELD HARMLESS, not growth per move (measured on the saddle-kit control: two strays stacked left of the pocket buy nothing individually and 155 sq in jointly, so a growth floor deadlocks; the plateau move unlocks the pair). Every accepted move lowers an envelope top by a quarter inch minimum, so the loop terminates; a0 -> a1 pocket numbers ride every move note; frozen columns (same part, same rotation, same left edge, 2+) never move -- proven by control, the saddle column stands while the squares fall into the valley BETWEEN the two large parts (Jordan's picture exactly). Honest limits: concave pockets are reachable only where both parts carry outer polygons (_pkt_ok stays bbox-conservative otherwise -- control: no outer, zero moves, never a guess inside a concave part); an alternate-180 PAIR of 2 reads as two rotation-singletons and may drop (4+ alt members freeze), the square-up's own shipped semantics. Measured: BG synthetic repro 4 pocketdrop moves, pocket 2302.2 -> 2338.1 sq in, used X unchanged 76.355; full-pack cost 25.9s -> 50.7s on that kit stated honestly, select_pack 141.5s unchanged (coarse + free exempt). Proven: test_pl_free.py 23 -> 31 controls (drop into the valley, frozen column stands, pocket grew >= 1400, audit judged the drops, no-outer refusal) + part_list 28, pl_select 21, pl_macro 25, pl_sync 23 (zero suite edits) + verify_plsel 20, plrefresh 18, refusal 16, pltabs 24, plhand 18 live. Zero dev O-numbers. SETTLE TO CONTACT + COLUMN CONTINUATION 2026-09-21 (Jordan's Nest_9-21-3 catch on his prod 44556B_57B_1 pack -- 'Looks to me like we're still missing 3 parts from the far right row. Figure out why, and change the code so it doesn't happen again'): the cause was MINE and twofold. (1) The free-drop scan (_pkt_find/_pkt_find_low) worked on the 0.5in grid step and took the first legal GRID spot, so stacked free drops sat up to a full step above true contact (pitch 5.0 where contact is 4.839). (2) Where a drop interlocks with a placed lattice column the legal x-window is NARROWER than the grid, so the scan missed the uniform column phase entirely and landed copies at drifted phases the grid could reach -- and the drifted neighbours killed the remaining slots DEAD (probed at 0.05in resolution: no rescue pass could ever win them back; the fix must prevent drift, never rescue it). FIX in part_list_nest: NEW _pkt_settle -- every accepted grid spot brackets and bisects down to true contact via the same _lat_contact arithmetic (axes y then x then y, fine step _PSTEP/8, every settled spot re-passes _pkt_ok); NEW column continuation -- every real placement writes a hint (exact x, top y, angle) keyed per part+rotation (_pkt_hkey), and the next copy tries the hint column FIRST, before the nofit memo (deliberate -- nofit records grid-scan failure, the continuation spot is off-grid): fine upward scan to the first legal y, then a bisect of the last fine step down to true contact, so a column continues at its own EXACT x and true pitch (without that bisect the pitch quantized a sixteenth high and the drift re-lost a slot -- measured 59 not 60); a failed continuation memoizes (hintdead, keyed hint + ceiling -- sound, a sheet's constraint set only grows, a new placement writes a new hint). refine= threaded through _pkt_find/_pkt_find_low and every consuming pass (rest-drop, rescue, frontier tuck, square-up, deep fill); pack passes refine=not coarse -- COARSE JUDGE PACKS STAY UNREFINED BY DESIGN (the selector's estimates rank candidates and only the winner's full pack pays true contact; paying it on discarded candidates measured ~3x judge time -- the first cut of this fix did exactly that and was gated same session); refine=False is the old arithmetic exactly, and _pocket_drop + the filler keep refine on (full packs only). Measured on the reproducing kit (his exact items through the server's own _pl_build: qty 50 rotate 180 Fill ticked, Mild Steel 0.04, 120x60 sheet, gap 0.2, standoff 0.15, lattice chosen): 57 -> 60 placed (50 asked + 10 fillers, was +7), the far-right stack a uniform 12-column at one exact x with pitch 4.839 true contact -- exactly the even rows he ruled -- used X 113.5295 vs prod's 114.0285 (slightly better), sheet count unchanged. His old layout's dead gaps stay dead (they were placed drifted); the fix lands on his job's next Pack again after deploy. Proven: test_part_list 37, pl_select 21, pl_macro 25, pl_sync 23, pl_free 42 (6m47 -- back at baseline after the refine gating; ZERO suite edits anywhere) + live verify_plsel 20, plrefresh 18, refusal 16, pltabs 24, plhand 27; dev engine restarted; zero dev O-numbers (packs never post). COLUMN JOIN 2026-09-21 (Jordan's Nest_9-21-s catch on the re-packed 44556B_57B_1 -- 'The upper-right most part appears to be in its own nest, rather than part of the right-most column ... I could see in a nesting algorithm that is designed for irregular nesting, but in this case it seems out of place to me'; Aristide's split-into-chunks instruction, Jordan's 'Go' -- chunk 1 of 4): he was right, and this entry's own 'uniform 12-column' claim above was OVERSTATED -- the shipped pack's far-right stack was 11 in column plus ONE stray at (88.64, 54.653), an inch left and off pitch. CAUSE (mine): the spill rescue places each part at the leftmost legal spot (_pkt_find x-major -- the spend-the-slab-last objective) and that greedy rule has no UNIFORMITY term; high up beside the flipped far column its taper opens a pocket one inch left of the eventual column phase, and the FIRST rescued part took it (no column existed yet -- the continuation hint is written by placements, and the column was born from the SECOND rescued part plus the fillers). FIX (part_list_nest.py): NEW _column_join pass, LAST among the on-sheet passes (the fill pass has built the columns it extends), full packs only, strategy 'free' skipped with the block (uniformity is not freeform's objective): a stray -- not in a same-part same-rotation column of 2+ -- whose envelope OVERLAPS a uniform column's next slot (one pitch above the top or below the bottom; overlap is the 'sitting in the slot, just off-phase' test, so a genuinely separate stray never moves) is offered that slot through the same _pkt_ok clearance arithmetic as every placement, and takes it only when the slot is legal and the envelope right edge stays within the sheet's used X (the slot x IS a placed member's own x, so used X cannot grow); irregular-pitch columns are never extended (no guessing); a thou-level outward escape scan absorbs the settle arithmetic's contact scatter, never a visible drift; on lattice only pocket placements move; moves recorded kind 'coljoin', never silent. MEASURED on the byte-reproducing 44556B kit: exactly 1 coljoin move, (88.64, 54.653) -> (89.64, 54.395), the far-right stack a TRUE uniform 12 (x spread 0.001, pitch 4.839 +/- 0.001), 60 placed and used X 113.5295 unchanged, and the clean free pocket GREW 91.8 -> 108.3 sq in (the stray no longer pokes into the top band -- the free-area product wins too). PROVEN: NEW test_column_join.py 13 controls (repro shape into top and bottom slots, separate stray never moves, irregular pitch never extended, clearance-refused slot stays, part-id and rotation gates, lattice pocket-only gating, successive-slot iteration, filler flag rides) + test_part_list 37 zero edits. Chunks 2-4 queued on Jordan's same Go: the long-suite battery + dev restart, then Spread X engine + door, then Spread X on the part-list Nest tab.
- •CLAUDE.md trimmed 2026-09-17 (Aristide 'trim it'): 99 build/fix records moved VERBATIM to docs/brick-history.md; CLAUDE.md 373 KB -> 142 KB, one summary line per record left behind record — Aristide caught the session window filling minutes after a compact; the audit found CLAUDE.md at 373 KB (~56,780 words) -- loaded whole into every session, roughly half the context before the first message. The trim (2026-09-17): the full verbatim records of UI bricks 1-37 and every follow-up build/fix entry (99 entries, ~265 KB) moved to docs/brick-history.md -- nothing edited, nothing dropped, git history holds the original file. CLAUDE.md keeps every standing ruling and rule (Rev2 master source, jobs-home filing ruling, the open work queue, staged briefs) plus ONE ~300-char summary line per moved record pointing at the history file. NEW STANDING DISCIPLINE recorded in CLAUDE.md beside everything-gets-a-pin: a build record lands in THREE places in the same commit -- the pin (authoritative prose), docs/brick-history.md (full verbatim entry, appended), and one summary line in CLAUDE.md. Never write a full record into CLAUDE.md again; never delete history, it moves verbatim.
- •No nested parens in NC comments (Jordan 2026-09-17): a paren inside a comment ends it early on the control -- every operator-typed name is bracket-sanitized at the emit boundary record — RULED AND BUILT 2026-09-17 (Jordan's O33342 header screenshot -- the job name 'CAMP#4 nest 36x60 (circled part removed)' rode into '(PART NAME : ...)' and nested parens: 'you can't put parenthesis inside of parenthesis'; on a Fanuc control the first close-paren ENDS the comment, so this is a machine-facing format error, not cosmetics): emit.nc_txt() turns parens to brackets in free text bound for an NC comment -- applied to part_name and label at the top of write_gcode_macrob (covers the O-line comment, the label line, PART NAME, and every nest sub header) and to pl_post's main label / PART NAME / per-sub part names. Typed header labels (_san_note) and warning text (nc_warn._san) already stripped or bracketed parens -- this closes the remaining door, the operator's own job name. Case and wording otherwise untouched; fixed literal comments unaffected. Proven in test_nc_warn's no-nested-parens control + header/progstyle/matrecord/pl_post suites green.
- •Operator PDF on every part-list delivery -- one letter page per sheet program + one dimensioned print per distinct part record — Jordan, 2026-09-17, third ask ('Again, I need a pdf of the nest/program for the laser operator, and this should be 1 page of standard letter paper'): every part-list delivery carries a PDF the laser operator can hold -- ONE page, standard letter -- showing the nest layout (true outlines, placement->part map per the 2026-08-31 rule) and the program facts (O-numbers main+subs, material/thickness, sheet size, counts, warning summary in the new grouped form). make_prints.py already draws letter nest pages for true-nest deliveries -- reuse its drawing, not a parallel renderer. NOT YET BUILT -- commissioned this turn, next build in the queue; multi-sheet jobs get one page per sheet (each still letter, the one-page promise is per sheet/program). SCOPE EXTENDED 2026-09-17 (Jordan: 'Where can I find the dimensioned printout for each part?' -- honest answer: nowhere in the app yet; dimensioned part prints exist only on the old bespoke board builds via make_prints, and the app's part-list delivery ships packet/customer/NC/DXF with no print PDF): the commissioned PDF therefore follows the standing nest-print ruling (2026-08-26) in full -- the sheet page(s) up front AND one dimensioned print per distinct part at the back, separately paginated, reusing the make_prints drawing machinery; lands with the same build. BUILT 2026-09-17 (Jordan's 'Go'): engine `pl_prints.py` (docstring = design record) renders from the job's own documents and REUSES make_prints' page machinery -- make_prints._part_fig split so part_fig_from_prims(prims,...) is the ONE dimensioned-part-page renderer shared by board builds and the app (never parallel implementations); engine prims convert to make_prints tuples in _mp() (a CW arc becomes the same arc walked CCW -- identical ink). SHEET PAGE per posted program: true-outline nest with the part labelled per contiguous group (make_prints' cluster rule; labels drop the shared filename prefix, noted on the page, so the identifying tail reads), nest X/Y extent dimensioned, O-number + every sub-program call (sub / part / rotation / copies, spill-guarded), material/machine/sheet size, and the GROUPED warning summary QUOTED VERBATIM from the posted NC header via nc_warn_header() -- the print proves what was delivered, never recomputed. BACK PAGES: one dimensioned print per distinct placed part (overall dims, lettered holes, hole table), the 2026-08-26 nest-print ruling. WIRED: run_pldeliver writes {stem}_operator.pdf into every delivery version (packet.json operator_pdf key), zip included; Deliver tab renders an 'operator PDF' button first in the row. Proven: test_pl_deliver 28 controls (new 8b: PDF magic + page count = sheets + distinct placed parts); CAMP v2 delivered through the running app -- 16 pages (1 sheet + 15 parts), 90 placements, O33385 shipped as 33385.nc. FIX same day (Jordan's 'Pdf is not working on my end' -- Chrome 'Failed to load PDF document' on the delivery URL): the delivery-file GET route served every non-html/json/zip file as text/plain, so the browser's PDF viewer refused the (valid) bytes; .pdf now maps to application/pdf in server.py's delivery ctype table. Verified application/pdf end-to-end through dev.kerfmaster.com; file itself was always valid (16 pages). LABELS FIXED 2026-09-21 (Jordan's Pdf_9-21-1 catch on his towing kit -- red text on EVERY part, covering the ink; the rule was one label per unique part per group): the cluster rule joined copies only when their boxes sat under a fixed 1in gap, and his spread layout's same-part gap measured ~1.18in, so every copy became its own 'group' -- contiguity is now judged by the nest itself (make_prints._label_spots: two copies group when NOTHING of another part lies in the rectangle spanning them and the gap stays under one part-size +1in, so a spread column is one group at any gap and a same-shape block past other parts still re-labels), and the label reads BESIDE the part (new make_prints._label_pos: right/left/above/below, first side whose text rectangle covers no part and stays on the sheet, labels already placed count as obstacles; a genuinely full sheet falls back to the old centred-on-the-part form -- covering the OWN part still labels the right part). Both surfaces, one machinery: the operator PDF sheet page and the board nest-print pages. Proven NEW test_label_spots.py 12 controls (towing repro 12 -> 6 labels, foreign-between splits, tight-gap parity, beside picks, full-sheet None) + test_pl_deliver 28/28 (2 pre-existing stale refusal wordings unstaled, suite-fix only) + rendered repro page and build-164 dense-sheet page read visually.
- •MetaCam Part Settings > Cutting preset -- per-shape-class START POINT defaults (circled), cut direction, tooling switches record — Jordan's screenshot 2026-09-17 (Defaults_9-17-1): MetaCam's Part Settings dialog, Laser CAM > Preset > Cutting, the Start point block circled in red. START POINT is a per-CLASS shop default -- three pickers, 'For outer shapes' / 'For inner shapes' / 'For circles', each setting where a cut starts by default, chosen VISUALLY (diagram: outer default = mid-right of the square, pierce dot outside, lead entering horizontally). Maps onto our recorded pieces: circles = start_clock 4:30 (already a setting destined for Preferences), outers = the lower-right BIAS (Jordan 2026-08-14), inners = today only the lead-aware clear-air chooser -- a distinct operator-settable inner-shape default is the piece our shop profile does not carry. CUT DIRECTION defaults: Outer CW / Inner CCW -- exactly the shop's G41-norm convention (holes CCW, outer CW); MetaCam's own default agrees with our recorded comp convention. SWITCHES: 'Retain all nodes of polyline in profile tooling' (off -- their face of our micro-segment/never-polyline cleanup); 'Convert tangent approach to 90 deg approach' (on) + 'Don't do this conversion at a line-line corner' (on) -- lead-in ANGLE policy defaults, ties to the per-contour lead angle override slot; 'Use marking for all open polylines' (off) -- open contours as etch, ties to mark-vs-cut; 'Suppress machine macro shapes' / 'Break macro shapes if L > 0 x W' (greyed) -- nest-macro knobs, ties to macro-per-orientation/main-program-style. Joins EDIT_TOOLING / Layout Array / Add-Toolpath / Basic-tab as a reference feature set -- record, don't build. Open with Jordan: whether the circled block is an ask (per-class start defaults as shop-profile settings seeding the chooser, biases staying biases) and the exact semantics of each class's picker.
- •Approach defaults -- scoped lead-in record per feature class + the three-way GROUP save both modals share, BUILT (Jordan 2026-09-20) recordmixed — Jordan, 2026-09-20: 'we still need to establish a way for the operator to choose default pierce/lead-ins which we discussed using screenshots from MetaCam' -- then his refinement on MetaCam's save options: 'each change can be saved for that specific material, or for every material listed under that machine. We could do one better... save for the specific material, all the material listed under that machine, or select a group of materials to save the default preference' (his example: inner lead-ins for .500/.625/.750 steel on the GX 10K, 'without affecting their other settings'). His 'Go' on the offered one-brick shape.
- •Start-point defaults -- sticky per-class engine anchor + scoped storage + magnet modal, BUILT (Jordan 2026-09-17) record — Jordan, 2026-09-17, on the MetaCam Part Settings reference (pin metacam-part-settings-cutting): 'A nice feature in MetaCam is the ability to set default pierce/lead-in locations... I can click and drag the pierce/lead-in shown, and it magnets to mid-points, quarter-points, and corners, and on circles it magnets to quadrants and 45's. I know we have a default set in your code somewhere, however, we now have evidence that it's not always applied. I'm hoping that writing it up this way that we get a modal for this too. It is going to be more sticky.' THE EVIDENCE, measured on his Detroit Wrecker 6-plate part-list job (prod): all six plates share the identical 17.38x3.25 outline, yet 4 outers pierce at bottom-CENTER and 2 (DE TOWING-PS, RJB-PS) at the bottom-RIGHT end. Structural cause read from emit's chooser: the ruled lower-right preference is the LAST tiebreak of a seven-term score (safety, clean, clear-air band, tipping/crossings ... lowright, right), so thou-level upstream differences between near-identical parts flip the winner -- the default is a tiebreaker today, not an anchor. THE BUILD (three pieces): (1) shop profile gains start_defaults per shape class {outer, inner, circle}, seeded from the recorded rulings (lower-right for shapes, 4:30 for circles), provenance recorded; (2) ENGINE goes sticky -- the class's default position wins OUTRIGHT whenever it passes every safety gate (pierce clear-air, tab-centred-on-flat, never a sharp corner, lead lands in scrap); the ranked hunt runs ONLY when the default spot is unsafe, and any departure is reported per feature (warn never block); safety tiers still outrank the default, the per-feature drag handle still overrides on top -- seed never bind; (3) the MODAL: one picker per class, drag the pierce/lead marker on a sample shape, MAGNET snapping to mid-points, quarter-points and corners -- quadrants + 45s on circles -- exactly MetaCam's gesture; lives with the shop profile (wizard/preferences), per-station theme-style persistence is NOT enough, it is a shop value. Identity risk named: the chooser is inside the planner fan-out hot path -- the build ships only with the parallel-vs-serial identity proofs and the standing fixture suites green. NOT BUILT -- commissioned, next build in the queue. ENGINE SLICE BUILT 2026-09-17 (Jordan's numbered go: wizard-asked BUT overridable per material/machine/job by right-click; 'it's a bias, not a hard coordinate'): emit.write_gcode_macrob(start_defaults={'outer'|'inner'|'circle': clock}) -- class follows MetaCam's split (true circle at any depth = circle, else outer/inner by depth); among candidates passing EVERY safety gate (usable lead, clean tab span, full standoff clear-air, zero held-slug crossings on the outer approach) the winner is PROXIMITY to the preferred bearing first, legacy score only breaking ties -- the anchor, not the last tiebreak; no gated candidate anywhere -> the ranked hunt decides and the contour row reports applied: False; a gate rejecting the nearest-to-preference candidate slides the anchor to the closest SAFE side and reports moved: True (proven on the approach-rapid-over-held-slug case); operator forced starts outrank and never read as departures; circles route the class clock through the quadrant rule. stats['contours'] rows carry start_default + start.source shop:start-default. start_defaults=None keeps every legacy path byte-identical (proven). test_start_defaults.py 13 controls; drag 23 / tooling_doc / robust 50 / plan_perf identity 10 all green. SLICE 2 BUILT same day (Jordan's 'Finish'): STORAGE -- shop.json start_defaults {shop, machine: {<machine>}, material: {<Rev2 key>}} + job.json start_defaults, every entry {clock, source}; RESOLUTION server._sd_resolve, job > material > machine > shop > the recorded rulings (seed outer/inner/circle = 4.5, lower-right / 4:30), the winning scope named per class; DOORS GET/POST /api/jobs/<id>/startdefaults (scope job|material|machine|shop, values {class: clock|null-to-clear}; material/machine key off the assigned review and refuse without one; junk plain-400, record-only -- the NEXT plan/post consumes it, the sheet-size precedent) and POST /api/shop accepts start_defaults (wizard = shop level; scoped overrides PRESERVED across wizard saves -- a larger scope never stomps a narrower one); WIRING -- all ten planning/post sites pass the resolved dict (dragcheck, review, quote count, single post, both postnest forms, _pl_build's per-orientation plans, plpost's per-part check + pl_post.write_sheet_subcall post_kw), so single parts, true nests and part-list sheets all anchor; WIZARD gains 'Where should cuts start?' -- three magnet shapes (square outer, square-hole inner, circle) live in the card; the MAGNET itself: SVG drag, marker snaps to corners, mid-points and quarter-points (quadrants + 45s on circles), stores the snapped CLOCK BEARING (12=top, 4.5=lower-right) -- a bias never a coordinate, side words shown; SCOPED MODAL from the feature drawer's 'start default ... change' row (the right-click dialog): that feature's class magnet + apply-to just-this-job / this-material / this-machine, Save / Clear-this-scope; VERDICTS surfaced -- Part review summary line (per-class default + winning scope + N moved for safety / N departed) and the per-feature row (applied / moved-for-safety / departed -- no safe spot), warn never block. Verified live on 44558B_10 (review verdicts 1 applied / 1 moved / 2 departed on the known narrow slots, post O33338 clean) and the CAMP part list (repack + plpost O33385 re-posted clean through the sd-wired seams); suites green: start_defaults 13, drag 23, tooling_doc, robust 50, plan_perf identity 10. Jordan's six DWS plates converge on the next re-plan: same outline, same gates, one anchor.
- •Warnings: short labels + class chevrons + operator green-flags + one-line NC summary -- BUILT 2026-09-17 (ruled same day) recordmixed — Jordan, 2026-09-17, verbatim rulings on warning verbosity: (1) SHORT LABELS -- 'right now they are too verbose. When a small hole... forces the lead-in to be too short and the pierce too close to the contour... All I would like to see is: "Warning: Short Lead-in". Now, make these clickable if the operator wants to have the detail, he can click on it and see your verbose warning.' The row shows the CLASS in a few words; the full engine message is one click away, never gone. (2) CLASS CHEVRONS -- 'right now you are already nesting under a chevron identical warnings. However, if they're even one thou different, I get 2 types of warnings. I'd rather see that all lead-ins regardless of the length in error still are compiled in one chevron.' Grouping keys on the warning CLASS (rule + severity), never on the message string's numbers -- one chevron per kind. (3) GREEN-FLAGS -- 'the operator needs to be able to click on the warnings, browse through them, and either individually green-flag them, or globally all of them in the chevron. This will prevent warnings in the gcode.' A green-flag is the operator's recorded sign-off on a finding (who/when once accounts arrive); green-flagged findings post NO callout. Warn never block stands -- the flag silences the callout, never the judgment. (4) ONE-LINE NC SUMMARY -- 'Rethinking about it. What we should do, if the operator chooses to not green-flag the warnings, is to put all warnings for a particular job in one line in the gcode. Example: "this file has 31 warnings"'. This SUPERSEDES the per-spot markers + numbered header list of pin gcode-warning-callouts (his own earlier ruling): un-flagged findings compile to ONE header line with the honest count; the detail lives in the app, not the program. BUILT same day (verification in the detail). EXTENDED 2026-09-18 (Jordan's Yellow-or-Green screenshot -- 'Why still yellow when I dismissed as green?'; Aristide 'Go'): the sign-off now SHOWS at a glance, judgment text unchanged. Flagged findings and fully-flagged chevrons render in the green-flag colour ('all green-flagged' wording); every 'N red / M yellow' count line carries a green suffix with the flagged share (shared sevCounts renderer: review/cutplan/post/postnest banners, part-list nest drawer part rows, sheet-wide judge, hand-layout judge); tab dots STAND DOWN to the done glyph when a flagged stage's every finding is signed off -- stageFindings reads the stage's own loaded document (review/cutplan/post/postnest/plpost/partnest), the tooltip stays honest ('flagged -- all green-flagged'), an unflagged or new finding puts the yellow back, and an unloaded document keeps the recorded status (honesty over guessing). Client-only (app.js/app.css); the NC form is unchanged. Verified live on the dev CAMP part-list: Nest and Post dots stand down with honest tooltips, drawer counts green-suffixed. EXTENDED 2026-09-20 (Jordan's 26342 'Go on both' -- his Q2: 'How can we have short lead-in warnings if I didn't change your default toolpaths? And why aren't they under just one chevron?'): (a) the pierce_standoff class label RENAMED 'Short Lead-in' -> 'Tight Pierce' -- the check is pierce CLEAR-AIR (rules.check_pierce), not lead length; the old name read as if the lead was cut short when the defaults were untouched. Label only; keys, messages and the NC form unchanged. (b) ONE CHEVRON ACROSS THE LIST: on the part-list Nest drawer the per-part plan findings roll into ONE chevron per warning class across ALL listed parts (previously each part's section grouped alone -- five parts with the same physics showed five chevrons). The per-part count lines stay; every row inside names its part (shared-prefix tail rule, full name in the tooltip) and still navigates/green-flags individually; the chevron header's green-flag-all keys on the class exactly as before. Client-only (app.js) + verify_warnnav suite label update. Proven live on the dev CAMP list: 73 findings on 15 parts = ONE Tight Pierce chevron, 73 rows each naming its part; verify_warnnav 10/10, verify_pltool 13/13, verify_pltabs 24/24; zero dev O-numbers consumed.
- •Global undo BUILT -- one stack, crosses jobs, navigates to the spot, heavy steps warn and ask record — Jordan, 2026-09-17, answering 'where are the undo controls?' honestly (a true Undo + Ctrl+Z exists only in the manual nest editor; everywhere else the model is recorded edits with per-row remove/clear): 'Undo should be global.' Ruled: one app-wide Undo (button + Ctrl+Z) stepping back the LAST recorded change regardless of which channel it rode in (heal edits, tooling overrides, sequence, start defaults, sheet size, nest edits, green flags...). SCOPE ANSWERED (Aristide, 2026-09-18): 'True global.' -- one journal, one stack; Ctrl+Z steps back the LAST recorded change anywhere, crossing screens and jobs (part detail <-> parent part list included). Cons stated to Aristide and accepted with their mitigations: every undo step NAVIGATES the operator to the spot it happens (Aristide, 2026-09-18: 'Can the undo not bring them to the spot it is happening? For each step?' -- ruled): right job, right tab, zoomed to the feature, the reverted value highlighted, so nobody ever undoes blind -- this supersedes the passive note+jump-link form; a short what-reverted label stays on the spot as confirmation, and a one-click return-to-where-I-was lands after an undo run; heavy inverses (undoing a pack-time edit = re-pack) ride the busy-feedback seam; an undo across the pack boundary may correctly stale-flag the parent or make the stale gate refuse a post; multi-operator rules (laser-side station undoing the CAM seat) land with operator accounts -- ties to the laser-side-station sitting; perceived-order surprise is answered by the named-undo note. Delivered versions stay immutable -- undo walks around them, never through them. HEAVY-UNDO CONFIRMATION (Aristide, 2026-09-18: 'Slow undo's -- Can we do a warning with a confirmation ask?' -- ruled): a quick undo (typed value, flag, label move) stays instant, no prompt; an undo step whose inverse is HEAVY (re-runs a pack or replan) first shows a warning naming what will be reverted and that it takes time, and runs only on confirm -- decline leaves the stack untouched, nothing half-undone. The confirm rides the same busy-feedback seam once accepted. Closes the slow-undo con: Ctrl+Z never nags on cheap steps, never silently stalls on expensive ones. BUILT 2026-09-18 (Aristide 'Go for this'): SERVER -- the journal hangs on the one persistence seam (_save_json + a recorded os.remove): every recorded-edit door (healedits, dims, units, sketch, sheet/vec extract, import rotation, review/tooling, green flags, start defaults, material record, common-line plan, sheet size, nest + all nest edit/seq/spread/restore doors, part list, pack, part rotation, pl seq/spread/hand) snapshots the BYTES of each file it touches, before and after, and a clean 200 journals the deltas as ONE step -- so an inverse is an exact byte restore, never a re-computation that could drift. One journal, one stack (jobs/_undo/, zlib blobs, ruled depth 200), POST /api/undo + /api/redo + read-only GET /api/undo; restores run under the affected jobs' own locks; a new edit forks history and drops redo. Deliveries, program numbers, shop tables, rates and customer filing have no door in the list -- they can never enter the stack. HEAVY steps (review/nest/pack/units/rotation re-runs, or any cross-job entry) answer needs_confirm first and run only on {confirm:true} -- the ruled warn-and-ask; decline touches nothing. CLIENT -- Ctrl+Z anywhere (Ctrl+Shift+Z / Ctrl+Y redo; text fields keep the browser's undo, the manual nest editor's unsaved draft keeps its local stack), topbar arrow buttons, and every step NAVIGATES: hash-deep-link to the job+tab, reload from the restored files, zoom+pulse on the reverted value when the step carries a point, toast naming what reverted, and a one-click 'Return to where I was' chip after the run. Verified live: NEW app/verify_undo.js 17/17 on the scratch instance (cross-job undo lands on the right tab, redo, decline-leaves-stack, heavy confirm, excluded doors, redo-drop, buttons, zero page errors); regressions verify_globalband 13/13 + verify_bandselgeo 13/13 + verify_bandsel 13/13 (nest editor local undo intact) + verify_snapdrag 10/10 + test_pl_sync 23/23. Dev restarted; prod rides the normal deploy.
- •Start ANCHOR constraint -- the customer's exact lead-in spot as a typed RULE (distance from a named corner), re-resolved on every plan; plus drag-handle magnets record — Jordan, 2026-09-17: 'if a customer tells us that for one particular part, they want the lead-in exactly .250" from the bottom right edge, is it possible to enable the operator to input a "constraint" ... that defines this?' His 'Go' covered both pieces on the table: (a) magnet snapping on the canvas drag handle, (b) the typed start constraint. Both BUILT same day.
- •COMPREHENSIVE TABLE EDITOR -- onboarding a new shop's tables + ongoing modifications (Aristide, COMMISSIONED) intent — Aristide, 2026-09-18: 'I want to build a comprehensive table editor we are going to be using when a different shop wants to onboard, as well as modifications we want to make to the tables.'
- •HARDCODED-VALUES AUDIT -- sweep the code for literals that should run through the tables (Aristide, scheduled tonight) intent — Aristide, 2026-09-18: 'Set up a test/audit you are going to run tonight to make sure that there are no other hard coded items that should be running through the table' -- born from the pierce_sub=9011 finding (Rev2 pierce_default/pierce_etch columns ingested 2026-08-24, consumed nowhere).
- •PRE-PIERCE PASS for small holes in thick material -- two-pass program (pierce-only nest, park rapid, (PAUSE) M00, then cut with quick pierces). GO GIVEN -- building intent — Jordan, 2026-09-18, verbatim: 'when we have small holes in thick steel and aluminum, the pierce is too close to the edges of the contour. A practical solution ... we will manually copy and paste our subprogram, and take out all of the cutting gcode, leaving only pierces. We run the complete nest piercing only. We will then put a M00 so that the operator can move the laser head out of the way and grind the material smooth from the pierce craters, then we go back to the original gcode and replace the standard pierces with a quick pierce (like the etch pierce P9019). Then we proceed with cutting, with everything already pierced this results in a clean cut without the slag debris.' His own sketch: 'Gcode will have 2 nests (P9997) with 2 different subprograms, the first of which would be pierce only, and noted for the laser operator that it is pierce only, ending with a M00. Then the second nest would be the complete subprogram with all the contours and etch pierces. Am I on the right track?'
- •Part-list <-> part-detail tooling SYNC -- Jordan's a/b/c go BUILT (2026-09-17): pack hands its plan to the child, child edits stale-flag the list, nest preview draws the true cut path; in-place envelope refresh 2026-09-18 -- the stale gate's remedy keeps the layout; refresh redraw fix 2026-09-19 -- the canvas caches rebuild on refresh, so the sheet draws the refreshed toolpath; auto nest update 2026-09-20 -- the stale layout updates itself on arrival, the operator is only stopped by a real decision record — Jordan, 2026-09-17, verbatim: 'Right now, when I look at my Part List tab, I see that tooling has been applied (lead-ins, etc.), but when I click on a part once, it brings me to a detail screen. It isolates this part nicely, however, all the tooling is gone. I even applied all tooling and made a change to see if the applied changes would come back to the part list screen once I exited the detail screen. It did not. We need to talk about this before we build anything.' REFRESH REDRAW FIX 2026-09-19 (Jordan's BG-BUMPER toolpath screenshots -- 'Changes in the individual toolpath vs translating to the nest don't seem to be happening. Am I missing something?'): the in-place envelope refresh updated the server record and S.plnest but never rebuilt the canvas caches -- plToolPath's as-cut Path2D per part x orientation, the dock points and the geometry paths were all built from the PRE-refresh plans, so after Refresh envelopes the sheet kept stroking the OLD toolpath while the pierce dots (drawn fresh from the plan rows each frame) moved; the fix is ONE MACHINERY -- runPlRefresh now runs the same loadPlPaths rebuild a pack runs, covering the Nest-banner click and the one-click Refresh-and-post alike. Client-only (app.js). Proven: verify_plrefresh 16 -> 17 checks (the new check counts the /geometry fetches the rebuild makes) + refusal 16, pltabs 19, pldock 14 green. AUTO NEST UPDATE 2026-09-20 (Jordan's workflow question -- 'is this user-friendly in its current state? If not, how would you propose we solve these workflow details?'; his '4. You are correct / Go' on the numbered proposal): the bookkeeping click is gone -- landing on the part-list Nest or Post tab (tab click, deep link or the return trip from a part edit) with part tooling edited since the pack runs the SAME layout-keeping in-place refresh by itself; placements, hand work, spacing and sequence stay, a quiet note names what moved, one undo step, and per his point-4 confirm a posted program still drops honestly on real drift, O-numbers kept. The operator is interrupted ONLY by the real decision: a moved toolpath that no longer fits the placed layout refuses in plain words naming the colliding placement (server wording reworked -- 'your moved toolpath no longer fits the placed layout'), the persistent refusal banner holding the manual button; a refused auto-run never silently retries (re-armed by a pack, a hand-layout nudge save, a clean update or a job switch -- plAutoKey). WORDING PASS on his point 5: 'Refresh envelopes' retired from the UI -- buttons read Update nest / Update nest and post, toasts read 'Updating the nest with your toolpath changes' / 'Nest updated', the drawer record line reads 'nest updated'; the undo journal keeps its Envelope refresh label (server record, unchanged). Client (app.js) + one server refusal string. Proven: verify_plrefresh reworked to the auto flow 17 -> 18 checks (landing auto-updates with no click; banner checks moved to the refused case) + verify_refusal 16/16 after a design-following rework of its stale-banner check (the landing now auto-clears staleness, so the suite re-stales and renders the banner directly; its first re-stale edit also had to change a CONSUMABLE value -- a moved near point alone normalizes to the same binding and honestly is not stale) + pltabs 19, pldock 14, srcdl 13, plseq 15, undo 17 (fresh scratch) green; dev engine restarted.
- •Job-level material pick + one-click APPLY TOOLPATHS on the part list -- material stays a fact of the PART, skip-and-name at a different material, the pack's mixed-material gate (Jordan 2026-09-20, 'Go ahead and build 1 to 3'); grouped per-material packing = the parked future road (his 'I'm going to think more about mixing materials') recordmixed — Jordan, 2026-09-20, verbatim: 'From the Part List, it would be nice if the operator can assign the material for the job and tell it to apply toolpaths per the default settings, for all parts on the job. Is this a good idea? If so, is it possible?' -- and on the mixed-material workflow question: 'In my mind, if I needed to group these into different material types, I would have sorted them into different jobs when uploading. This feels right to me... however, I can't say that's going to be true at other shops.' His go: 'Go ahead and build 1 to 3. I'm going to think more about mixing materials. Good to know it's a possibility.'
- •Laser-side operator station -- KerfMaster at the machine, operator edits live in the SYSTEM (Jordan 2026-09-17; sitting scheduled, permissions open) intentmixed — Jordan, 2026-09-17, verbatim: 'In our shop, and this may or may not be the case for others, but I plan to put a laptop or tablet by the laser for the laser operators, with KerfMaster on it, linked together with the rest of the team: sales and cad. This way, if the laser operator wants to make a change to a job, like moving lead-ins, etc., they can do so on the fly. The idea is that the improvements they make to get better gcode also now live in our system versus just living in the laser. This will give us one source of truth when we get a reorder years later for a job. We're not going to execute on this now, but add it to the pin list.' THE IDEA: today the laser operator's fixes (a moved lead-in, a nudged pierce) get typed into the control and die there -- the program in the laser drifts from the program in the archive, and a reorder years later re-cuts the UN-improved version. A station at the machine running the same app closes that loop: the operator's edit rides the job's own override seams (per-feature drag, startdefaults, sequence -- all already recorded consumables) and the re-post becomes a new immutable version on the job chain (job-versioning ruling), so the archive IS what cut. Fits the standing architecture with nothing new invented: one source of truth / three lenses (a tablet is just another renderer over the same engine + JSON state), the two-step flow (programmer vs laser operator are different people -- the reason warnings travel IN the NC), and the jobs-home ruling that the relationship dictates who may put work into production. NOT COMMISSIONED -- Jordan explicitly: record now, execute later. SITTING SCHEDULED (his ask, 2026-09-18): discuss in detail (a) how the laser-operator role enters the flow -- where in the queue model their edits land, what re-posts/re-delivers on an on-the-fly change, how the floor sees 'the program changed under you'; (b) PERMISSIONS around the role -- what a laser operator may touch (their own tooling moves?) vs not (material tables? delivering? prices?), which lands on the operator-accounts thread (per-user attribution 'arrives with operator accounts' is stamped on half the app's records today; windows-installable already inventories mandatory accounts). Open questions, not answers -- nothing here rules the permission model.
- •Vanished towing posts SOLVED -- stale-plan refusals read as completions after a mid-evening deploy; O33343 posted; refusal banner + one-click Pack-again-and-post BUILT; deploy clobbers prod's progseq (open with Sem) + the undo-journal clobber (2026-09-19, partially fixed) recordmixed — Jordan 2026-09-18: 'I did Post it in the Post tab, and it completed (more than once).' SOLVED same night, forensics over the read-only /api/diag door (shipped 4013c793) plus running the doors directly. CAUSE: his pack ran 2026-09-17 20:34:55; commit e42e0a78 (start-anchor, changes emit's override binding/lead arithmetic) landed 20:41:11 and deployed minutes later; every Post click after that hit the plans_match stale gate -- 'the packed layout predates part DWS-LH-2.5-LOGO-TREAD-BRONX-DS.dxf, bnd 21 pierce moved 0.0039 inch since the pack reserved its envelope' -- a 400 shown only as a transient toast during a ~minute busy overlay, so it read as a completed click with nothing to show. Every trace agrees no towing post ever persisted: pl_onums None (only run_plpost/_drop_plpost write it, NOTHING deletes it), job updated frozen at the pack, .lock mtime 01:32 = his refused retry, the same refusal reproduced live pre-repack. The gate itself is CORRECT (seq-display-is-the-program: a program must match the reserved envelope); the silence is the defect. FIXED LIVE: re-pack + post through the public doors -> O33343, 1 sheet 12 pcs, 69 yellow 0 red, persistence verified by file mtimes + stages + onums. His 9-17 'gcode + PDF' memory = the CAMP 33385 re-post + v2 delivery + operator PDF, all on DEV (the PDF content-type record names dev.kerfmaster.com; dev CAMP mtimes 16:17). OPEN 1 (commissioned on Jordan's next go): refused Post/Deliver persists on the drawer in the warn/red role until resolved, never toast-only, plus one-click 'Pack again and post'. OPEN 2 (escalate to Aristide + Sem): cadmaster/app/progseq.json AND ~1563 old dev job files are TRACKED in git, so every prod deploy overwrites prod's shop program-number sequence with dev's copy (proven byte-identical incl. used_once 33385 which never ran on prod) -- duplicate-number hazard; mitigated 2026-09-18 by advancing dev's file to 33343 to match the handed-out prod number; the real fix moves runtime state out of the mirrored tree WITH a prod-side migration (a naive git rm --cached would delete prod's live sequence file on the next pull and reseed at 33289). Prod-created jobs (towing, CAMP prod) verified untracked -- real customer state never rides the mirror. OPEN 1 CLOSED 2026-09-18 (Aristide '3' on the numbered queue): PERSISTENT REFUSAL BANNER + ONE-CLICK 'PACK AGAIN AND POST', client-only (app/web/app.js). A posting door's plain-400 refusal deliberately records nothing server-side (the job's documents are untouched), so the CLIENT now keeps the record: S.refusals per door (post / postnest / plpost) holding the engine's own words + the clock time, rendered as a red REFUSED banner at the top of that door's drawer section ('nothing was posted; any earlier program stands unchanged', the words in a drawernote beneath) until the door next runs -- an answer carrying a job record (clean or blocked stage, where the recorded block banner takes over) clears it, and a job switch clears all. The single/nest doors also no longer undefine S.job on a plain 400 -- runPost/runPostNest assigned r.job unconditionally, so a refused request used to blank the whole client state. ONE-CLICK: when the refusal's own words say 'Pack again' the banner offers 'Pack again and post' (runPlRepackPost: captures any typed program number BEFORE the pack re-render drops the field, re-packs via the same runPlPack -- one machinery, posts only if the pack landed a clean layout, lands on the Post tab showing the programs); the Nest tab's TOOLING-CHANGED stale banner carries the same button beside Pack again. Sheet 1 keeps its O-number across the one-click (the pl_onums rule). Verified: NEW app/verify_refusal.js 16/16 on scratch 8479 (clean post no banner, refused post persists + survives tab switches, engine words under the banner, one-click offered only when the words say Pack again, packs/posts/lands/keeps the number, Nest stale banner both buttons, single + nest doors via stubbed 400s, S.job survives, clean-run clear, job-switch clear) + neighbours green: pltabs 18/18, plseq 15/15, undo 17/17 on a fresh scratch, busy 14/14. Three suites fail without this build too -- tab-unification staleness, proven: verify_plpost check 10 fails identically on the stashed baseline (program rows moved to the Post tab; baseline also flaked check 5), verify_pldeliver waits for pldeliverbtn on the Nest tab while committed HEAD renders it only on Deliver, verify_partlist checks 1+9 expect the old two-tab strip while HEAD tabsFor ships the ruled seven -- queued to unstale with nestedit2 + groups. OPEN 2 (progseq/job files tracked in git) unchanged, still with Sem. SECOND CLOBBER CASE MEASURED + PARTIALLY FIXED 2026-09-19 (Jordan's 'The call undo failed' on Ctrl+Z over his duplicate Add): the global-undo journal (app/jobs/_undo -- journal.json + 601 blobs) was TRACKED in git, so every deploy overwrote prod's undo journal with dev's -- prod's Ctrl+Z stepped DEV's journal (dev absolute paths, dev blob hashes) and failed. FIX: _undo untracked from the repo and cadmaster/app/jobs/ gitignored WHOLE, so no new runtime file can ride a deploy again; the next deploy clears prod's poisoned journal (the stack restarts empty -- undo works from the operator's next edit; the server tolerates a missing journal by design). STILL OPEN with Sem: progseq.json + ~1563 old dev job files remain tracked (removing them could orphan prod work in those dirs -- prod-side migration required, unchanged).
- •FULL job-style unification COMPLETE: stages 1-5 BUILT (picker, fork door, one-road uploads, door convergence, suite migration + door retirement) (2026-09-19) recordmixed — Jordan 2026-09-19: 'we are treating single-part jobs and multi-part jobs like 2 different programs... Is there a logical way to unite these job styles into one, user-friendly, logical workflow?' Measured: seven duplicated server doors (nest/nestseq/nestedit/postnest/delivernest vs partlist/plpack/plpost/pldeliver), two hand editors -- purely historical, bricks 3-9 predate the part list. RULED (Jordan leaning Full, deferring the go; Aristide same day: 'I agree, that is the way to do it. this way you can go back in history right before the fork if necessary, and the history is never lost.'): every job IS a part list; a single-part job = a one-row list with qty. The true-vs-part-by-part ruling survives intact (the list road plans every part before packing, so the one-row case keeps toolpath-first), and the 2026-09-19 macro-runs build posts the true-nest G172+G65P9997 macro form off a uniform layout -- one door, both forms. Old jobs keep opening on their old doors, deliveries immutable; only new uploads take the one road; old doors retire gradually, never deleted. SHARING SEMANTICS RULED (Jordan '4. I agree with your recommendation.'): REFERENCE BY DEFAULT -- the part is the master, an edit to it flags every listing job through the built tooling_fp stale gate; 'copy as new part' is the EXPLICIT fork, never the default (Aristide's history point: the fork preserves pre-fork history forever). STAGE 1 BUILT same turn (record, client-only app.js/app.css): the ADD-EXISTING-PART PICKER -- run_partlist's by-reference door always accepted any healed job; what hid it was the flat unlabeled row dropdown. 'Add from existing jobs...' on the Part list tab opens a modal running the Jobs-home machinery (customer folders, search over part name/id/order reference/program numbers via jobONums), already-listed parts badge 'listed -- row k, qty q' and a second click refuses (edit qty on the row instead); a picked part lands as a DRAFT row (qty 1, rotation locked, table bridge) -- Save list + pack sheets stays the record, and saving still drops a packed layout per the standing stale rule. NEW app/verify_plpick.js 17/17 DRAFT-ONLY on the real 7497/7498 job (a reload proves the saved list on disk untouched; Save never clicked) + pltabs 19, partnav 15, ctxmenu 26 green. STAGE 2 BUILT 2026-09-19 (record; Jordan 'Go for stage 1' -- stage 1 already shipped, read as the staged plan's go): the COPY-AS-NEW-PART FORK DOOR. SERVER run_fork, POST /api/jobs/<id>/fork: the part AS IT STANDS -- source.dxf, geometry.json, review.json, plus healedits/tooling/dims/sketch/cutplan where present -- copied byte-for-byte into a NEW independent job carrying forked_from {job, name, date} provenance; a stage record survives only when its artifact came along (review/cutplan keep their status, nest/quote/post/deliver honestly read pending -- a copied O-number or delivery chain would be a forged record); lineage markers (split/split_from/variant_of) stay with the ORIGINAL, the plvariant door's precedent -- forked_from is the fork's one provenance; refusals in honest words, nothing created (part-list job = 'fork a PART, not the list', missing job 404, blank name, no source geometry), and the original stays byte-untouched always. CLIENT: every picker row carries a quiet 'copy as new part' control (hover-visible, deliberately dimmer than the default reference 'add'; the plain row click stays the reference) -- name prompt seeded '<name> copy', the fork lands as a DRAFT row and the picker shows 'copy of <original>' lineage; cancel at the prompt creates nothing. NEW app/verify_fork.js 24/24 (per-file byte-identity, original job.json byte-unchanged, refusals create nothing, draft-only on the real 7497/7498 job with reload proof, suite scratch jobs removed) + plpick 17, pltabs 19, partnav 15, ctxmenu 26 green; engine restarted. STAGES 3-5 INTENT, in order: (3) single uploads land as one-row part lists (the grouped-upload tray's group shape with one member -- new jobs take the one road); (4) Nest/Post/Deliver converge on the pl doors for new jobs and the true-nest editor retires with them (the standing true-nest snap-rewire offer FOLDS IN here rather than running separately); (5) suite migration + door-retirement records. Each stage its own RECORD commit. STAGE 3 BUILT 2026-09-19 (record; Jordan 'Go for stage 3'): ONE-ROAD UPLOADS, client-only (app.js -- zero server change). A SINGLE upload now rides the grouped-upload tray's group shape with one member: upload -> the first-open units question fires AT the door and the wrap WAITS for its answer (Confirm, flip or Later chain through a one-shot continuation, so a flipped file re-heals BEFORE its parts become rows) -> split behind the scenes (the split door's own 'single part' refusal means the file IS the part; a multi-part kit honestly lands as an N-row list) -> a part-list job named after the file stem, one row per healed part (qty 1, rotation locked, table bridge) -> every member bound to the group -> the LIST opens on its Part list tab. ONE MACHINERY: the tray's stack flow and the single road share the new roadGroup(), and a lone tray card is now a one-member group too (customer filing lands on the LIST). The direct door survives only where the road has nothing to list yet, honestly: a blocked intake (no empty list created), vector art awaiting its stated width, a detected drawing sheet awaiting extraction -- vector and sheet jobs JOIN the road the moment convert/extract lands healed (idempotent via the group guard, a re-convert of a wrapped job never re-wraps); the sales surface keeps its stripped flow. roadGroup also stops listing a detected-but-unextracted drawing sheet as a row (its heal judged the SHEET -- the tray previously listed it silently). Old jobs byte-untouched; only new uploads take the road, per the ruling. NEW app/verify_oneroad.js 17/17 on the scratch instance (units at the door both paths incl. the confirm-chains-into-wrap case, 2-part kit -> 2-row list with split_from provenance and the file job bound but never a row, blocked file keeps the direct door with no empty list, several files still open the tray with nothing uploaded); verify_multiupload REWRITTEN 12/12 (it had been stale on the pre-tray batch toast since the 2026-09-14 tray build) + verify_upload_formats updated 10/10 (STEP and converted-AI one-road expectations, conversion note asserted on the part's own drawer) + tray 17, plpick 17, pltabs 19, partnav 15 green. Stages 4-5 remain: door convergence (snap-rewire folds in), suite migration + door-retirement records. STAGE 4 BUILT 2026-09-19 (Jordan 'Go on stage 4') -- DOOR CONVERGENCE, client-only routing, zero server change (server doors stay). A job bound into a group IS a row on a part list (run_group enforces group.id = a part-list job), so its own true-nest doors stop being OFFERED: the member's Nest / Post / Deliver tabs stay on the strip but render a pointer at the list ('this part is a row on X' in honest words per tab) with a one-click open that lands on the list's MATCHING tab (partnest/post/deliver -- the hash names the screen, the job change is one history entry, browser Back returns to the member); their stage dots drop (stagesFor returns [] -- the LIST carries the sheet stages' dots, Jobs-home cards included). Two honest exceptions behind ONE gate (doorsConverged, a pure function of the job doc so drawer and home cards cannot disagree): a job with NO group (old jobs, picker-referenced parts) keeps every door byte-identical, and a member whose nest/post/postnest/deliver/delivernest stage stands beyond pending keeps its doors too -- recorded work (and its immutable deliveries) is never orphaned; the server seeds every stage key pending, so the test is STATUS, never key presence (even a blocked refusal is a record the operator must keep seeing). Geometry and Tooling on the member stay untouched -- per-part work is the point of opening a row. The standing true-nest hand-editor snap-rewire offer FOLDS IN by retirement: new jobs never reach that editor (the one road's pl hand editor carries the toolpath dock), old jobs keep the box-envelope snap as a legacy surface -- the rewire will not be built. NEW app/verify_converge.js 17/17 on the scratch instance (fresh member: three pointer tabs, no dots, landing + Back; ungrouped job keeps Run nest; a job group-bound AFTER a real review+nest run keeps its doors with the done dot; the list's own pack door intact) + oneroad 17, multiupload 12, upload_formats 10, tray 17, plpick 17, pltabs 19, partnav 15 green. Stage 5 remains: suite migration + door-retirement records. STAGE 5 BUILT 2026-09-19 (Jordan's 'Go on stage 5' -- suite migration + door-retirement records; the pin's plan is COMPLETE, stages 1-5). THE AUDIT FINDING: every old true-nest suite creates its jobs by direct API POST (api/jobs?sketch=1 or a raw DXF body), and the one road is CLIENT-ONLY at the upload door -- so those suites were never on the road by construction; their jobs stay ungrouped, doorsConverged stays false, and every door renders byte-identical. ZERO app or engine change in this stage; one-road coverage lives in the five new suites (oneroad 17, converge 17, tray 17, multiupload 12, upload_formats 10). THE UNSTALE PASS (suite fixes only; every failure PROVEN pre-existing fixture/expectation staleness from engine-era drift since the suites were written Sep 1-7): (a) delivered-NC warning wording has two eras -- immutable old versions carry (CONTAINS N WARNINGS), new posts (THIS FILE HAS N WARNINGS); verify_deliver + verify_delivernest accept either. (b) the postnest engine now emits per-column macro arrays, serpentine part order and bare {onum}.nc delivery filenames -- verify_postnest (structural array checks, G65 count = arrays length), verify_nestseq, verify_multisheet updated; nav209's dropped nest.json re-posted THROUGH THE DOORS first (qty 196 alt_flip, O33292 kept, subs 75292/75293). (c) version dirs pad to three digits -- v00${v} broke at v010 (verify_deliver). (d) the brick-13 tour card overlays a fresh Playwright profile's canvas and eats clicks, and raw legacy tab names bypass the merged-tab alias -- verify_seq gets km_tour_done + tabAlias. (e) nav209's review now plans CLEAN -- the start-defaults + lead-aim work cleared its old clear-air findings -- so verify_warnnav's warning phases move to ex2-dirty (4 yellow + 2 red) and expand the green-flags chevrons before visibility waits. (f) contour IDS are plan-order indices and RENUMBER on every re-plan; the override channel's own key is the centroid -- verify_tab now tracks the edited feature by c.at (the verify_propagate byAt pattern), re-selects by current id per phase, and waits on the busy seam instead of findrows; two 0.05 tab-override leftovers this class deposited on nav209 were cleared through clearOv (the outer's recorded 0.03 operator tab stands -- recorded work is never deleted on suspicion). (g) findrows() now folds ONE chevron per warning CLASS (rule+severity, Jordan's ruling) and rows show short class labels with the verbose message on the fvTog detail link -- verify_findnest's synthetic findings carry the rule field and expect x8-in-one-chevron; the detail text is asserted URL-decoded off the link. (h) verify_onum made self-sufficient (posts 9501 explicit, restores the kept 33292 via use_once). FULL BATTERY GREEN: postnest 12, nestseq 17, multisheet 15, seq, onum 6, deliver 12, delivernest 13, warnnav 10, tab 11, findnest 8, healqc 14, partlist 14, seqbadge 11, gseq, handsafe, stampdel, groupsnap 9, homedrag 14, traverse 10, draghandle 13, plpost 14, pldeliver 14, pltabs 19, nestedit, nestedit2, groups, units 18, sheetsize -- kept numbers preserved (33291 single, 33292 nest). DOOR-RETIREMENT RECORD: for NEW uploads the true-nest doors are unreachable from the UI -- the one road wraps every upload into a part-list group and door convergence points members at their list; the direct door survives only where nothing can be listed yet (blocked intake, vector awaiting stated width, detected drawing sheet) and those join the road when convert/extract lands healed. The true-nest hand editor is retired for new jobs (the pl editor carries the toolpath dock). The SERVER doors stay, deliberately: old jobs, recorded work and API callers keep them byte-identical -- retirement is a UI fact, never a server amputation.