What We Shipped

Changelog

Every commit. No marketing spin.

1–30 of 512 commits
1 / 18
The Nov 12 images were built before the FluidNC commit, so their manifest still said 30f62729.
Inside-first cut order (card cmwdzajwm08ho01m4peiq8t41): a part nested in another part's hole is cut before its host again, in Plasma, Laser, Oxyfuel, Router and Tube. Lost in 306e247d (Mar 19). Shared common/cam/part-containment.js, per-workspace part-ordering.js, permanent contract test + real Ash Can keyhole fixture, docs/Plasma/inside-first-cut-order.md. No runtime post-time check by decision.

Kerf offset double lap (card cmwdzag3d08hm01m415v82wh7): fitArcs placed a prior over claimed ring points and the assembly wrapped, emitting the Lid Plate loop twice; the torch re-ran its own kerf. Claimed-point trim/reject, fail-closed assembly, turning-number + perimeter invariant, 0.002 in same-circle merge in clipper_closed; raw-loop fallback is marked degraded and warned.

Probe source race (card cmwdzab3408hk01m49xziv840): a [PRB:] arriving before the pin status is 'unobserved' (#<_probe_source> 3), not 'ambiguous'; fire_torch v10 and the Touch program re-probe floating-head only instead of prompting. Optional firmware PRB 4th field preferred when present. Existing v9 subroutines keep working unchanged.

Soft-limit alarm (card cmwdzaotl08hq01m4x811psau): ALARM:2 names the axis, target, travel and program line; position-suspect alarms make Home the primary action. Root cause was a Z servo alarm during THC, not firmware.

Also: Plasma sim program-model uses the shared arc tessellator; Plasma hotkey can-play goes through the Simulator tool (e1efb12d contract fixes). Staged FluidNC firmware images.
One Scribe Type select covers a pneumatic actuator on one output (motion-synced
M62/M63 + down delay), a Z-mounted scribe/engraver and a spring scribe threaded
onto the torch (manual Z0, retract/scribe height, plunge feed, dwell). Every call
forces the torch output off; Dry Run and torch/oxygen output collisions never
energize the scribe. The engrave cycle now runs Scribe Off once before the first
engrave rapid so a Z-type scribe zeroed on the plate never drags. Engrave tabs
(Plasma/Tube/Oxyfuel) point to the bundled templates when a machine lacks them.

Card cmw9zsf6p01uj01m4zcktypfh (Promach field report: fire_torch picked as Scribe On).
- Machines tab lists only connected machines (views/machine-list.js); the machine
  the phone just left stays, marked Offline; an empty list names both fixes incl.
  the exact Settings -> Nearby permission label (test-asserted).
- Jog speed: touch-slider.js replaces the tiny native range input. 56 px track,
  touch anywhere / drag (haptic tick every 10 %), -/+ nudge 5 % with hold-repeat,
  role=slider keyboard support.
- Smoke test: speed slider step (26 -> 80 -> 85 asserted); X+ hold centres the
  button first so it can't land on the tab bar.
- Docs, v2.6.1 notes.
- segmentSkeleton: every click locks out to the nearest cut edge (_clickFlood).
  Barrier = black-hat thin dark lines + Canny 60/140 step edges (pieces < 0.4 in
  dropped); plate and floor clicks flood with Dial's queue, barrier cost 5000;
  sure = leads by 0.3 in without crossing a barrier. Floor flood keeps the
  luminance band; floor pieces guarded (edge, solidity, splits). Enclosed-patch
  claim kept for mottled stains. Replaces the edge-wand/multi-click/floor wand.
  First skeleton 22/22 holes at both resolutions, shadowed 17/17.
- Trace step: hover a click dot; same button removes, other flips, drag moves;
  per-change undo history.
- JC3 Mobile: a request on a dead-but-not-closed socket reconnects and waits
  up to 4 s instead of failing "Not connected to JetCad3"; stale close events
  ignored; nudge() reconnects dead sockets. net.test.js.
- Docs, v2.6.1 notes, replay --ridge-dump.
- Several plate clicks in one bridged floor patch (<= 8 % of the raster) claim it
  together unless a floor click sits in the same patch (_floorPatches, built once).
- A lone plate click grows a magic wand on CLAHE-equalised luminance with the
  strong Canny edges of the plain photo (60/140) knocked out. Edges from the
  equalised photo were 63 % of pixels and boxed every click into a few-pixel cell.
- Measured and rejected (docs/snaptrace.md): whole-photo equalisation as the trace
  input (merges cut-outs through thin webs), texture grain/coherence as a split
  feature (stain and brushed concrete measure alike).
- Photo-step contrast tip; dev "Copy clicks" under More (plane inches) and replay
  --clicks / --edge-dump / --warm / --preview-side; texture probe script.
- Two new real-OpenCV tests. Both real photos unchanged (20/19 and 17/16 holes).
- Rectangle square-up uses four pre-placed draggable corner handles (phone too)
- Skeleton flow in Plasma/Laser/Oxyfuel/Router/Tube: left-click plate, right-click
  floor, quick preview while clicking, precise full-resolution trace on Finalize,
  then drag/delete/add outline points; progress badge on the photo
- segmentSkeleton: data-driven split + locked local click regions with guards;
  GrabCut seeded for reproducible traces; per-tap work bounded to region boxes
- Vision client: per-request timeout, one trace in flight; shared trace-view.js
- Real-OpenCV tests on synthetic plates modelled on Travis's photos; replay tool
  --method skeleton / --preview
- Taps are hard GrabCut labels (4-px eroded core, soft rim) — a 'not part' tap on a
  shadowed cut-out was being re-absorbed by GrabCut's colour model
- Mask boundary smoothed (σ 1.5 px) before contouring; RDP 1 px, arc fit 1.5 px
- estimateCameraPose (single-view calibration from H) + correctWallParallax lift the
  trace from the cut wall's bottom edge to the top surface; Material thickness select
- Replay tool: --thick, --crop full-res overlay, JetEngine trace output
- Tests: pose/parallax, hard-tap regression on real OpenCV
- feature-policy: BETA_WORKSPACES (Tube, Lathe, eCAD) read 'beta' instead of the
  tier pill when the tier can open them (locked rows stay not_available), matching
  the website's Beta flag. Tube leaves DEV_ONLY_WORKSPACES and is switchable in
  release builds for every tier that can use Plasma; the dev-only mechanism stays.
- Workspace menu: amber Beta pill with a hover note; dev fallback flags updated.
- Send To Tube follows the switchable flag in release builds.
- v2.6.1 release notes: Tube (Beta) section and the Beta badge line.
Card cmw5q4iv302tg01pgo40udyoj.

JC3 Mobile
- JC3 advertises <name>.local over mDNS (multicast-dns) so home-screen icons
  survive DHCP changes: probe/rename on clash, per-subnet answers, re-announce on
  IP change, goodbye on stop. Settings -> Mobile -> Advanced "Phone address name";
  .local URL first, IPs as fallback with a DHCP-reservation tip, QR can use an IP.
  No cloud/jetcad.io dependency by decision (air-gapped shops).
- lanAddresses() skips VM/container/VPN adapters (bridge*, vmnet*, utun*, ...).
- Phone app: no pinch/double-tap zoom, no rubber-band; Add to Home Screen steps on
  the phone and in Settings -> Mobile; follows a custom desktop theme via tokens.
- Phone photos are nameless (phones send "image.jpg"): state/time/phone instead.

SnapTrace
- Desktop photo loader decodes bytes -> ImageBitmap (jc3media:// images tainted
  the canvas: "getImageData ... cross-origin").
- Segmentation replayed on Travis's real skeleton photo (scripts/snaptrace-replay.mjs,
  the M0 tool). Two real bugs: GrabCut's label view detached by WASM heap growth on
  megapixel images (result read as all background); GC_* constants now literal.
- auto = colour+brightness (Lab 2-means, K=8 families); extra taps are a magic wand
  bounded by the kerf line; background taps; GrabCut edge refinement on by default;
  hole floor 1/4 in. Trace view zooms/pans; green mask; phone capture scrolls to
  the measurement fields after the 4th corner.
- Regression tests: brown-on-blue colour split, slug wand, 2.7 MP GrabCut.
Card cmw5q4iv302tg01pgo40udyoj (M1-M13; M0/M14/M15 acceptance and Learn sync remain).

JC3 Mobile: opt-in LAN HTTP+WebSocket app (port 49255) with shop PIN
(vault scrypt), per-device sessions, QR join, lockout, Host/Origin guards.
Settings -> Mobile tab. Phone photo inbox, Agent Tower relayed through the
owning renderer tab, data-driven pendant + GcodePilot-style DRO via
PendantBridge (externalStep / explicit-feed externalStick / externalStickStop),
and Nearby machine relay under the new machine_control permission.

SnapTrace: one homography module (shared/snaptrace/geometry.js) for
rectangle, soapstone-marks and ChArUco-board squaring with references and
lens profile; OpenCV warp/segment ops; shared capture stage (phone + desktop);
desktop trace dialog; shared SnapTrace CommandTool on Drafting, Design,
Plasma, Laser, Oxyfuel, Router, Tube (Workspace._sharedTools honours
static workspaces). Laser/Oxyfuel/Router gain setPlateShapeFromExternalGeometry.
CamTrace transfer moved to common/camtrace/transfer.js (old path re-exports).

Docs: docs/mobile.md, docs/snaptrace.md, pendant.md, CLAUDE.md choke-point row
+ homography contract test; v2.6.1 release notes.
Website card cmw77rcie05ak01pg30nh9dlv. Eleven tools (4 read-only: list, get,
entries_list, entry_get; 7 writes: create, update, set_status, entry_set_status,
award_trial, send_winner_email, mark_shipped) over the website's
/api/admin/giveaways routes with the existing super-admin bearer. Writes are
documented as explicit-direction-only; settings/lifecycle are revision-fenced.
Docs: website-backlog-bridge tool table + discipline, CLAUDE.md, live-debug-bridge.
Operation buttons were evaluated only at activate() and setup cycling. A file
opened while already in Lathe left every Turning button disabled (its active
setup arrived after the toolbar last checked). refreshScene() runs on every
document change, so it now refreshes enablement too.
- Selecting operations shows only their toolpaths (overrides eye states
  without changing them); clearing the selection restores eye visibility.
- Post: with ops selected the button reads Post (N ops) and posts just those
  in setup order (even if disabled); right-click Post / Post N Operations.
  A partial program always goes through Save As and never replaces the
  remembered output path.
- Simulate with a selection: earlier ops' removal is fast-forwarded into the
  stock, playback starts at the first selected op and completes at the last
  one. The daemon runs the full program with earlier op blocks blanked to
  comments in place (closing rapids kept) and the program truncated after
  the span, so line numbers still map onto the removal model. Frames past
  the span end never advance removal; a partial span is not saved as the
  setup's remaining stock. Scrubbing outside the span disarms it.

Verified on the real JetFlight daemon: mid-program start, early span end,
cold G33 thread start, single op and a span with unselected ops inside it
all run with no errors and no off-path frames, stopping at the last
selected move.
- Dynamic Orbit raycast only the model group; the simulation stock and tool
  live in the sim group (model stock hidden), so the pivot fell back to the
  spindle centre. Include the sim group while simulating.
- Stock chunks use the exact surface raycast: a chunk wraps the whole
  circumference, so Mill's AABB-only raycast would pivot up to a radius off
  the surface. 5.6 ms per gesture on the machined acceptance part.
- Collisions: timeline markers + red tool only (Mill's); no error notices.
Contacts were recorded but surfaced only as the timeline's small event dot,
so the boring bar rubbing the bore went unnoticed.
- The drawn tool turns the theme danger colour while the cursor is on a
  colliding move: holder/shank for holder, shank, jaw, fixture or tailstock
  contact; the insert for a rapid into stock.
- Red markers on the timeline track at each collision's program time
  (opt-in eventTime on the shared SimTimelineBar; Mill unchanged).
- One error-indicator notice per operation and contact kind, naming the op,
  Z and depth.
- getPosition() still holds the last frame of a stopped run when the next
  run starts. A run stopped at Ream made the fresh driver jump straight to
  Ream (full sweep, Ream tool mounted), then every real frame from line 19
  was off-path. Capture the snapshot before starting the session and ignore
  it until the new run moves.
- External shank top was coplanar with the insert top (both y = 0), which
  flickered. Seat the insert half its thickness proud of the shank.
- The drawn tool mounted only when the cursor reached an op move; the
  program prelude has none, so no tool appeared until removal locked on.
  Fall back to the executing line's op, then the first op.
- A DRO that never lies on the posted path (a JetFlight build without G7
  diameter mode / simulated spindle) now raises one clear simulation error
  instead of silently leaving the bar untouched.
- Legacy chuck records that declare jaw_count/jaw_tip_radius_in on the body
  (no jaws record) now draw and collision-check those jaws.
Simulator toolbar gains a Coarse/Standard/Fine select (persisted, applied
at the next simulation start; tooltip shows the running grid). The report
shows the effective Z cell, angular cells and spatial bound.
- shared/lathe/chuck-library.js: the one reader of a Lathe machine's jaw
  chucks (2-8 jaws, body, jaw width/protrusion/radial length, grip range,
  external/internal grip, stepped jaws); geometry edits bump the revision.
- Machine Configuration > Workholding: chuck list (add, duplicate, default,
  remove) beside a dimension diagram that highlights the measurement each
  field means (common/dimension-diagram.js + chuck-diagram.js).
- Setup > Workholding: pick a machine chuck plus grip diameter (clamped to
  the chuck's range), chuck face and clearance. The resolved geometry is
  snapshotted into the .jc3 with chuck_id/revision; stale snapshots are
  flagged in the browser and updated on request. Records without a chuck id
  load unchanged as a custom chuck.
- Jaws drawn at their real width and grip side, with step shoulders; jaw
  collision windows come from the jaw width at the grip radius.
- Automation: lathe.chuck.list and lathe.fixture.create_from_chuck resolve
  through the same shared function.
- revolve-tool-mesh: fixed facet count (arc-tessellation contract).
Completion sweeps only the unswept tail. Checkpoints every 5 s / 400 moves
stay within the 160 budget across the 30-minute acceptance program. On the
real acceptance paths, a mid-program restore plus fast-forward and
one-move-per-batch playback both reproduce the straight run cell for cell.
- Swept convex hull per move (exact per-theta-cell helix for G33) replaces
  per-sample stepping; first-touch journal, value snapshots, targeted restore.
- Conservative slice bounds give O(1) rapid/holder skips; work budget 20M.
- 64-slice mesh chunks with station compression, nearest-to-tool ordering.
- Coarse/standard/fine grid presets (1024/2048/4096 z cells).
- Face cuts keep their signed X past spindle centre; the kernel folds
  far-side sections onto physical radii, so no false centre pip.
- Contact floor drops quantization-scale grazing; internal tools reach in on
  a bar; face grooving turns the tool 90 degrees.
- Real acceptance-part fixture + perf harness: full 1,798 s program sweeps in
  under 1 s at standard (~2,000x realtime).
Tools are now laid out physically from their existing library fields: true
insert outlines (80/55/35° diamonds, trigon, square, round, groove, thread)
at the shape's usual holder lead, with the nose radius rounded and centred
on the programmed point; a square shank running radially away from the part
with the insert seated flush at centre height; boring bars along the
spindle with the nose proud of the bar; parting blades thinner than their
insert; drills and reamers revolved with their point. Rear-toolpost tools
draw on the far side mirrored, inverted tools upside down, and editing a
tool redraws it.

The removal/collision kernel receives the same insert outline plus separate
holder-head and shank regions (previously the holder ran along the spindle
and the shank region was the insert itself). The path generator's profile
is unchanged, so no toolpath changes. Older stored tool snapshots derive the
new geometry on the fly. Card cmw5ua397031v01pgx69x837h.
Transport: Mill's signed speed ladder with the zero rung and model-driven
reverse playback; the shared simulation hotkeys (Space, Esc, -/=, arrows,
Shift+arrows) with tooltip shortcuts; a speed chosen while paused is now
sent on resume; FO/RO persist; IN/MM badge.

Removal: the driver follows Mill's protocol — the cursor advances on every
DRO frame and sweeps chain behind it, so the tool is drawn at the executed
position immediately instead of after each frame's removal finished. The op
tool is mounted once per op change (was every advance). Checkpoints are
chained through the driver at Mill's 5 s / 400-move cadence with a 160
budget and thinning; scrub, step, op-jump and reverse share one
restore-and-replay seek. Completion sweeps the posted tail instead of
re-running the whole program. The loop runs on animation frames with a
timer fallback for hidden windows. Card cmw5ua397031v01pgx69x837h.
common/sim/ now owns the simulation hotkeys and signed speed ladder, the
adaptive mesh-pump tuner, the AABB stock raycast + O(1) bounds, and the
revolved tool mesh. Mill imports them with no behaviour change. Settings ->
Keyboard gains a Lathe scope; Mill and Lathe ids now enter conflict checks
(Mill's group had no CONFLICT_GROUPS bucket). Lathe tools join the tool-id
contract. Card cmw5ua397031v01pgx69x837h.
An orphan .o left by a deleted source (dynamic_toolpaths v2) made the bar
read 217/202, and the silent -O3 -flto link looked like a hang on the last
compiled file. macOS seq 1 0 also drew two stray empty blocks on a full bar.
While the PointTrace dialog is open, a per-machine PointTrace key layer
(gp_pendant.pointtraceBindings) takes over the keys it assigns: capture,
Line / 3 Pt Arc / R Arc / 3 Pt Circle, next/previous construction, Close
Loop, New Contour, Remove Last, Undo/Redo, cancel pending, R Arc flip/length.
Unassigned keys keep their normal job; abort/feed hold/resume/start-pause
keys, the Fn modifier and the gamepad deadman are locked.

- shared: pt* actions (context 'pointtrace'), per-layout suggested layers
  (stock WHB04B-4/-6 + gamepad; Promach and others empty),
  effectivePointTraceBindings, pointTraceLockedKeys, single pickBinding
  resolver used by the bridge and the gamepad poller
- bridge: per-machine PointTrace context over gcodepilot:pendant:pointtrace,
  cleared on dialog close, renderer destroy/crash and pendant disable
- renderer: pt* actions run through the same PointTraceController methods
  as the HUD; capture waits up to 1.5 s for a jog to settle; Ø Circle stays
  screen-only (3 Pt Circle from the pendant)
- HUD: PENDANT badge, key chips, pick ring, green/red flash, large status
  strip, WebAudio success/failure tones with a toggle, Pendant keys… link
- Machine Config → Pendant: Normal / PointTrace keys switch, locked rows,
  per-layer Restore preset, PointTrace cheat-sheet page

Promach behavior is byte-identical with the dialog open and an empty layer
(new transcript test); freeze snapshots untouched.

Card cmw5lbsph02lc01pgep958z7w
A session id the server no longer holds (the app restarted or the session
closed) returned 400, which clients treat as a bad request. They kept the
dead id: connected status, every call failing until a manual reconnect.
The Streamable HTTP spec requires 404 here; that is the client's cue to
start a new session. A missing id on a non-initialize request stays 400.
- Sheet section -> Material & Process (material, process at the stock wall, quality); no
  thickness/width/height/origin/offsets/quote
- Sheets -> one Parts list; no sheet rows, Add Sheet, Bump Stop or dead + From Design
- + From Drafting opens a Drafting BODY picker (Flat Sketch... keeps the wrap path)
- no flat sheet mesh / labels / origin marker in the Tube scene
- machine without a Tube chuck axis is flagged where the machine is picked
- job sheet thickness follows the stock wall; a fresh flat wrap no longer reports an 'older document'
- scripts/tube-acceptance.mjs + vitest wrapper (opt-in via JETFLIGHT_BIN / TUBE_ACCEPTANCE=1): source -> part -> nest -> toolpaths -> JetFlight Tube post -> real daemon; checks timing vs bound, THC/mark events, M170 at rest, WCS restored, sim drop mapping
- fixes: wrapped holes NaN (tube-features), sim line base off-by-one (tube-sim-program)
- docs/Tube/README rewritten (workflow, choke points, file map, limits), acceptance.md, stale statements fixed
- reported JetFlight/GcodePilot defects pinned with it.fails tests: G64 P + G93 chuck overspeed, single-block G93 section uses previous feed, GcodePilot STREAM split resets modal state (WCS/units/feed mode)

Card cmw1ilf8p04p801s9kl1ovrz8 (epic cmpyuqdvc000901pa88keuhdr)
1–30 of 512 commits
1 / 18