Repository navigation
Conversation
…uency; the idle baseline follows the spindle up Dry run 9a2697a71a2f (2026-09-29): the spindle at S8000 sits on a soft-start plateau of ~7480 RPM (125 Hz) for its first ~15-20 s, then holds 8000 loaded or not - so "loaded vs unloaded" was wrong, it was time since M3. The X rapid also sings a 125 Hz series, so a rapid with the spindle commanded on read as 7480 RPM. - During a RAPID the gantry comb is also fitted unmasked; a spindle candidate on one of its harmonics (either way round, within 2 %) is `ambiguous`: recorded, never RPM. Feed moves are left alone - while cutting the spindle IS the strongest comb - and rapids carry no load. - The idle baseline is the median of the most recent 3 s of settled free frames, so the plateau is replaced by the settled speed before a cut is judged, and a cut that starts too soon after M3 shows the plateau as a reach error rather than hiding it. - Synthetic free phase is a still dwell by default (`freeKind`), with a `gantryFree` tone option; tests for the ambiguous rapid and the rolling baseline across the plateau. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… sags are judged against the idle baseline Job beb4bf0a3d22 (2026-09-29): the panel's RPM stopped two passes before the cut ended. The timing model treated every G0 as a 3000 mm/min rapid, but on the A350's CNC firmware a G0 travels at the modal F once one has been set (81 mm returns took ~9.7 s = F500), so the model ran ahead, hit the queued line - which is 16 blocks AHEAD of the head - and was capped there; the spindle context reached M5 while the head still had passes to run, and the tracker stopped. - executionModel: G0 duration uses the modal feed when known; the head is bounded to the block `lead` behind the queue (no further back than the blocks queued since the last sync) plus one block of slop, and may only close the whole gap when the queue is frozen at a sync or the file's end. - spindleTracker: sags are judged against the rolling idle baseline (the operator's rule and the reference script); the best sustained loaded median is reported only - a single high mis-lock cluster made it 8280 on this job and every honest 8000 read as a 3.4 % sag. Tests updated to the rule: a spindle that idles low and cuts at S is HOLD with a reach flag, one that idles at S and drops 8 % under load is STRUGGLE. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… real head Job b3e8f3dd0e34 (2026-09-29 22:08): the operator stopped the file 5.9 s in, the spindle had never turned, the head was 55 mm into its Y traverse - and the tracker reported a 7472 RPM lock plus a "not reaching" event. Three defects, all fixed from the recordings of that job and of d8006b17b237: - ExecutionEstimator.setStartPosition() was never called by the session: the leading G0s were timed at 0 s and the model sat on the dwell after M3 from t = 0. The session now takes the head's pre-job position from the last heartbeat, and the modal feed the controller still holds from the previous file (the A350 runs a file's leading G0s at it: a 106 mm Z descent took 12.7 s at the inherited F500, not 2.1 s). - inferExecutingLine() walked onto the comment after the 20 s dwell, so the frozen queue looked live and syncFollows() said no; it now stops at the last sync line of a non-motion run, and a head reported at a sync can be re-matched to the segment it just finished. - There is no spindle "soft-start plateau": the 124.5 Hz comb read as 7477 RPM during every post-M3 dwell was the Z axis descending at F500. Its second harmonic is 15-17 dB above its fundamental, while the spindle's fundamental is always its strongest low harmonic (idle 8010: 1x +29 / 2x +23 dB; cutting 8000: +26 / +15). A candidate whose 2x is >= 10 dB over its 1x (5-frame median) is `gantryLike` and never a lock, whatever the estimated context says. Replayed: 0 locks on the level 6 traverse and on level 4's Z descent; every frame locked on the real spindle from 2 s after M3. Also: a `flags` column in the audio ring (locked / clipped / contested / ambiguous / gantry-like), the analyser's `latestSpectrum()` (96 log bins, 30 Hz-8 kHz) in live.json, and the camera panel's octave-band bars - which saturated whenever the cut or the gantry got loud - replaced by a scrolling spectrogram with the spindle 1x / 4x and the gantry tone marked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Eval 17 in the cnc-motion-rules suite: plan a safe bed and workpiece survey on the PR #219 build with the touch probe fitted (~75 mm), camera alignment at safe heights, a coarse survey, then a fine survey of the enclosure's tailstock end for a later screw hole. The workspace records iteration 1 at bbef661: fixtures modelled on the box on 2026-10-04 (not homed, B180, no active tool, no camera model), the live tools/list, four dry-run plans and the grading. opus 17/20, sonnet 15/20, haiku 4/20, sonnet without skills 11/20. Every model missed that an active tool lets camera routes run below the Z320 floor against stored landmarks only - it is undocumented. Force-added under the ignored .claude/skill-evals/ like the other eval workspaces; scheduled for removal in the 2027 purge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A self-contained brief for the agent that will update the CNC skills and MCP docs after iteration 1 of the bed survey eval: the results, thirteen PR #219 behaviours verified in the source (the active tool overriding every protrusion source and opening sub-floor camera routes, camera_program's op shapes, fit_calibration storing the 2x2 matrix rather than a camera model, unhomed refusals, where bootstrap frames live), gaps G1-G20, proposed skill and doc wording, product changes for the operator to decide, and how to run iteration 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add camera-operation guidance, exact composite schemas, remote capture/index retrieval, persistent requested snapshots, and independent bootstrap holdouts. A safe enumerated programme uses one approval, including its initial Z move; bootstrap now establishes its advertised park height before taking any XY leg. Keep the motion floor independent of fitted-tool assertions, scope clearance exceptions to individual operations, recheck camera envelopes, preserve partial evidence on failure, and invalidate stale fitted-tool authority. Explain probe conversion versus clearance, legacy boxes, physical planes, future-cut datums, probe reach and event budgets. Rebuild distributable skill archives. Update future eval assertions without running agent evals. Do not infer full rotary obstacle volumes from axis points, physical plane corrections from seam drift, or calibration validity from an inverse-matrix check.
A CircuitPython/Blinka-compatible monitor (vibration_monitor.py) streams LSM6DSOX / LSM6DSO32 / ISM330DHCX tagged-FIFO samples at the chip's ODR, or polls any Adafruit-driver IMU, behind optional TCA9548A muxes. The pure Node side decodes its batches, re-measures the real sample rate, and analyses each sensor: Welch PSD, levels (g, ISO 10816 velocity, displacement), tones with direction, a harmonic comb for spindle RPM and the non-harmonic (chatter) tones, spatial periods for a moving axis, activity segmentation and a position profile, tilt against a baseline, and the rotary chuck's absolute angle from a fitted gravity calibration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The feed runs the monitor over its own Blinka bridge or a CircuitPython board on serial, refusing the probe feed's USB bridge. Captures record windows (with look-back), keep raw float32 samples on disk for re-analysis, and optionally record every file job. Seven read-only tools: get_vibration_status, capture_vibration, get_vibration_capture, stop_vibration_capture, list_vibration_captures, measure_rotary_angle and calibrate_rotary_accelerometer. Settings -> MCP Server gains an Accelerometers section; get_stored_state and job status report it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README section (hardware, bridges, configuration, what the analysis can and cannot say, files and timing), per-tool reference, optional poll-mode driver requirements, and a cnc-vibration skill with recipes for axis noise, toolhead deflection, tailstock chatter and rotary absolute B. CI runs the monitor's simulated-sensor Python tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd feed RPM comb: refine from the line within +-2 bins (a lone clean line read up to 1.5 bins low) and count every harmonic's weight (a 2-flute cut locked an octave high); half-rate ambiguity is a real line at f0/2. Envelope time is the whole-sample window. Captures no longer drop live samples as look-back duplicates, mark every new monitor run as a gap and map windows through a time index, re-analyse in yielding chunks, write meta at start and read the controller's B raw instead of through the position-of-record judge. A rotary reading is still only with 3 whole seconds, low time-domain vibration, no gravity drift and a bias-free gyro; calibration needs 4 readings over 180 deg. The feed allocates rings lazily, closes a serial port that finishes opening after close, strips inherited BLINKA_* and refuses the running probe feed's bridge; settings restart it only on a real change. Configurations the bus or memory cannot carry are refused; the monitor flushes before an overrun and pairs accel/gyro counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR review: a reconnect is now a gap (per-sensor streams outlive their monitor in the pure vibrationStream.ts, and a settings change ends the captures recording them). Every poll sensor goes through the mux-aware adapter, a root sensor may not share an address with a muxed one, and a corrupt FIFO word ends the batch and marks the next as a gap. Poll gyro is recorded. The bridge check becomes explicit resource leases (USB bridge, native I2C pins, serial ports) against the stored and running probe feed, with exactly one bridge selector. Several CircuitPython boards run as separate links. Raw samples are written asynchronously with backpressure, a global active budget and a disk budget. Tests: eight interleaved streams through a restart, eight sensors over four mux channels, root versus open-channel isolation, bad-tag gaps, multi-board and lease rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page sent its keepalive only at the end of a full status poll, and every poll enumerated serial ports. With the 500 ms poll and skipped overlapping polls, a slow enumeration left about a second between keepalives and the server disarmed with the one-second heartbeat message, often mid-jog. - Send the keepalive from its own 200 ms timer while armed and visible. - Cache the serial port list for status polls (2 s); connect still lists fresh. - Name the failing field in rejected frames, e.g. feed 60 outside 300-3000, and log the rejected line once per new error. - Firmware resets feed to the imported SLOW_FEED rather than a literal, sends its build id (logged on connect) and rare link/write events. - LUBAN_PENDANT_TRACE=1 logs every USB line in both directions for debugging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of the decoupled keepalive found it could keep a session armed while the page itself was frozen. - Stop keepalives once status has not rendered for 1.5 s; a failed poll clears the armed flag, and polls started before an arm cannot clear it. - Use AbortController with a timer instead of AbortSignal.timeout, which Electron 15 (Chromium 94) lacks. - Status polls wait at most 1 s for a port enumeration and otherwise reuse the last list; the cache is stamped when enumeration finishes. - Keep live values out of rejected-frame reasons so a bad stream logs one line rather than one per frame; truncate firmware events to 120 chars. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operator rules for the feed (mm/min): - 300 when the Feather boots; nothing else resets it. - Twist in feed mode adjusts it within 60-3000 (minimum lowered from 300). - Entering Z mode caps it at 1000; a lower feed is kept, and returning to feed mode keeps the capped value. Arm, disarm, STOP and USB loss no longer reset it (they still require neutral before the next jog). - The host rejects Z-mode frames above 1000 as well as anything outside 60-3000, naming the limit. Firmware id pendant-2026-10-05b. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pendant page now draws a plan view of the bed in machine X/Y (Y up): known travel, the reviewed or armed envelope, each obstacle exclusion labelled with its required Z, and the toolhead (live dot, or a held ring when the DRO is not verified). A table lists each exclusion with the stored height, basis and notes. Obstacles the envelope reaches below their required Z turn red, with a warning beside Arm naming the obstacle, its footprint and required Z. An obstacle refusal no longer disarms. The offending segment is never sent; Luban stays armed and sends only motion that stays clear: the approach up to 0.5 mm outside the exclusion, or the stick intent with the offending axis component removed. Every transmitted segment still passes the complete obstacle check. The hold is reported as `blocked` in /pendant/status and the USB DRO frame, mirrored beside the map, and shown on the Feather TFT bottom row while linked (red BLOCKED, amber LIMITED, scrolling past 38 characters). It clears when the stick is centred or a full segment is accepted. The first hold per obstacle per arm is logged. There is no landmark override. A pre-fw Feather is now logged once per connection as unidentified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…idence A failed tool call could leave the controller in G53 or G91. Every call now records its direct sends on a per-call modal ledger. When a call fails and may have left a non-preferred mode, the registry sends G90 and G54 as separate, axis-free commands, but only while the call still owns the machine, on the same connection generation, with no other operation, job or pendant active, the machine idle, and no stop, trip or crash alarm latched. The original error is preserved and a failure_recovery block reports attempts, skips, outcomes, connection identity, verified or unverified modes and position trust, pointing at restore_work_frame whenever recovery is incomplete. Nothing is retried, and inert tests cover the scenarios in the issue. Closes #221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operator decision: a segment with X and Y unchanged (within POSITION_EPSILON_MM) and Z strictly rising is permitted regardless of obstacle boxes, in the final validateJogSegment check itself; the envelope and travel Z limits still apply. Arming inside an exclusion below its required Z is now allowed so the pendant can climb out. Until it leaves, `blocked` reports `inside` with "INSIDE <name> below Z<n>: Z-up only" (page, DRO frame and TFT, shown while linked even with the stick centred), and every other motion holds, including Z-up with any XY component. Review fixes for #227: - The approach pad (0.5 mm XY, 0.1 mm Z) now applies to the first hit test and the axis-drop candidates, per box whose padded volume does not already contain the start, so short segments stop at the pad instead of creeping to the exclusion edge. Only validateJogSegment uses the exact test. - Targets are rounded to the 3 decimals moveMachineSettled sends, kept inside the armed envelope, before any check, so the validated numbers are the transmitted ones. - Displayed heights round required Z up and requested Z down to 0.01 mm (runtime text, page map and warning, TFT). - An axis drop never removes the dominant stick axis, so a Z-down twist with slight XY drift holds instead of sending only the drift. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ool text Refs #312 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… firmware view RayController is the only firmware whose 0x01 0x21 trailing SN layout is known (#315), so the Ready-time adoption is limited to the Ray, and the identity block in the firmware view to GRBL dialects: an A350 whose UI read its machine info still reports exactly what it did before. Refs #265 #312 #315 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th M5 Ray firmware V1.7.4 never reports laser power (0x12 0xa1 / 0x12 0x01 are always 0, #315 mismatch 2) and nothing sets headStatus, so every toolhead-on guard passed on the Ray whatever the laser was doing. - gcodeDialect: a ToolheadReadback per row; the Ray's is 'unknown' with M5 as its laser-off line, every other row 'reported'. - toolheadState.ts (pure): one assessment feeds every guard (camera move and home, move_z, traverse_xy, laser tools, probing, tool setter, workspace, pendant). Unknown is reported, never read as off; a non-zero reading still refuses. - gcodeForms.relativeStep leads every Ray step with M5 (laserPlan opts out, it manages its own M5/M3/M5); home sends M5 as its own payload before the unchanged $H. - An advisory MCP-sent-only laser record (M3/M4 with S > 0 and no M5 after), in get_position, get_machine_profile and motion results on the Ray only. A350 and Original output is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
start_gcode_job's direct-step result now carries the toolhead block with laser_off_sent on the Ray (and a laser_off_sent job event), so the staged traverse_xy, goto_work_origin and laser bodies report the M5 they sent, as move_and_capture and home already do. Tests pin goto_work_origin's M5 lead and that the validator's verdict on a Ray traverse is unchanged by it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Run the first-connect read and report the Ray's SACP reply limits Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Treat the Ray's toolhead state as unknown and lead its motion with M5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
start_gcode_job required file upload and start for every job kind before branching on it, so every approved direct job on the Ray (SACP over UDP) and the Original (text serial) was refused with "does not support file job submission" (live 2026-10-19, jobs 36fa11c67b4e and 7a19f87b186a). The check is now per kind, still before the token is consumed: a file job needs upload and start, a direct job or procedure needs executeGcode. stop_gcode_job requires only the channel's stop. The A350 channel has both capabilities, so its path is unchanged. goto_work_origin's staging result now carries confirm_url and next_step like traverse_xy, and a source-level test checks every staging site. Closes #318 Refs #265 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #318 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Start direct jobs on channels without file upload and return goto_work_origin's confirm link Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In Luban's Manual Mode (SACP 0x01 0x48 motor power SHUTAll) the Ray V1.7.4
ACKed $H with result 0 and never moved; MCP home timed out after 120 s
(live 2026-10-19). Control Mode (STAYPOWER) homed it in about 4 s.
- gcodeDialect: a motorPower readback per row; only ray-grbl reads one
(sacp-0x48), every other row is 'none', so the A350, the Original, the
Artisan and the J1 send no 0x48 request and are not gated.
- motorPower.ts (pure): a bounded (3 s) GETCURRENTMODE read that never
throws, the mode decode and the refusal. Only STAYPOWER passes:
RayController 91a675f runs SHUTAll and ONLYLINEAR through the same code
(motion_platform.cpp:461-472) and the steppers only treat STAYPOWER
differently (Stepper.cpp:474). Unknown refuses ("motor power unknown").
- SacpChannelBase.getMotorPowerMode: read-only accessor (GETCURRENTMODE),
so the MCP never holds a setter. The MCP never switches the mode.
- Gates, read fresh each time: home before anything is sent (M5 included),
start_gcode_job and policy-approved transport before the approval is
consumed, and move_and_capture before it sends.
- get_position and get_machine_profile carry motor_power on the Ray.
Closes #321
Refs #265 #315 #319
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to Control Mode Luban homes the Ray when Set Origin Mode changes to Control Mode, and a Ray direct step is a relative increment planned from the position before that, so starting the refused job afterwards would not reach the approved destination. The refusal, the start_gcode_job text and TOOLS.md now say to withdraw it with stop_gcode_job and restage; the approval is still left unconsumed. Refs #321 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Refuse Ray home and job starts while its motors are off Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A direct job records the judged machine position at staging. On a relative-steps machine (Ray, Original, J1) start_gcode_job, and every later step of a batch, refuses before the token is consumed when the position differs by more than POSITION_EPSILON_MM from where the step was planned from (staging, then the previous step's settle line), or the machine is unhomed or its position unreliable. The A350's absolute G53 steps are not checked. Closes #323. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
stop_gcode_job (and the dashboard's Stop) on a direct batch with no step in flight now withdraws it: the token is cleared, the job ends withdrawn and nothing is sent. A running step keeps the firmware stop where the channel has one; over SACP-UDP and text serial the remaining steps are withdrawn, the running step finishes, and the result says a running step cannot be stopped by firmware command over that transport. A job a firmware stop ended mid-step is no longer re-armed when the step returns. Closes #320. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A withdrawn or ended direct job reached the head-position check before consumeToken and was refused as 'the head moved since staging' when the head had moved. It now gets the token's own refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oval-free settings
set_motor_power {mode, reason} (Ray only) stages a confirm page: control
sets STAYPOWER over SACP 0x01 0x48 and then homes with M5 and $H, as
Luban's Control Mode does; manual sends M5, sets SHUTAll and latches the
position unhomed until a home. The channel setter is reached only from
this tool's approved job or the operator-only setting.
Two per-machine settings on the Motion rules pane, default off and never
writable through MCP: "Allow moves without an approval page" (traverse_xy,
move_z, goto_work_origin, goto_tool_change_position, a laser-off
laser_frame) and "Allow motor-mode changes without an approval page".
With one on, the action runs on the call only with the agent's quote of
the operator's instruction, recorded on the result and the job; every
other gate still applies. Both are reported in applied_records. Closes #326.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also say on the Motion rules pane that relative-step machines stage every move unless the new setting is on, and name home as the way out of the released-motors latch after a switch in Luban. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Re-check the head position before starting relative-step direct jobs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Withdraw direct batches between steps instead of sending a firmware stop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Feature: Switch the Ray's motor-power mode and add operator settings for approval-free moves Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nels at staging On the Snapmaker Ray (V1.7.4, SACP over UDP) submit_gcode_job staged and the operator approved a laser file job, and start_gcode_job then refused it with "The connected channel does not support file job submission" (job 8595d76f9e33): SacpUdpChannel had neither uploadGcodeFile nor startGcodeJob. Luban's own Ray UI (RayUploadWidget) uploads the job by the compressed SACP transfer as ray.nc and tells the operator to press and hold the work switch; the firmware (RayController file_manager.cpp) has NO start command for an uploaded file - hmi_req_cb_start_file_print ACKs "need use press button" - and only starts /ray.nc from a long press. So the MCP now does exactly that: - SacpUdpChannel: uploadGcodeFile (compressUploadFile to ray.nc with a 30 s inactivity watchdog; a lost result is `uncertain`, a refusal `rejected`), stopGcodeJob (the SACP job stop 0xAC 0x06 Luban's Stop sends) and fileJobTransport() = operator-switch start, file-print-only stop. - SacpChannel records the 0xAC 0x1A/0xA0 job progress into the machine state as gcodePrintingInfo (SSTP's shape), and re-reads the file info on the idle -> running edge of the legacy heartbeat. - gcode.ts: a new `armed` job state after the upload (nothing sent, holds the machine); the heartbeat watcher adopts the run for ray.nc (SYSTEM_STATUS_ STARTING counts as active, /ray.nc == ray.nc), progress and completion as on the A350; a lost upload result is start_unverified (never retried); stop withdraws an armed job (ray.nc stays on the controller - said so), sends 0xAC 0x06 to a running one, and sends nothing to an idle Ray (that stop alarms outside a file print) - so #320's "no firmware stop for a direct step" semantics hold although the channel now has a stop. - submit_gcode_job REFUSES at staging on a channel that cannot start file jobs (Ray over USB, Original, Artisan/J1 over Wi-Fi), naming the channel and machine, so no approval is ever asked for a job that cannot run; the confirm page carries a STARTED AT THE MACHINE banner on the Ray. - Dashboard: armed jobs listed under running with their own label. - The A350's SstpHttpChannel is untouched. Tests: fake SACP client (sacpConnection.test.ts) for the upload target, rejection, silence watchdog, stop and printing info; fileJobStart.test.ts for the staging refusal, armed -> started -> completed, stop while armed/running/ idle, uncertain upload, Manual Mode refusal, direct-step stop; jobEnding for the armed stop plan. Fixes #328 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Run MCP file jobs on the Ray over SACP (upload ray.nc, operator-switch start) and refuse incapable channels at staging
…metry tools
The release tooling job failed for v4.15.2-mcp-alpha.9 ("No category for
tools/enclosure.ts"), leaving the alpha.8-era 65-tool list in the notes.
- Categorise enclosure, motorPower, laser, telemetry and vibration tool files
- Scan cameraProgram.ts (camera_program is registered outside tools/)
- Resolve `name: TOOL` constant names (set_motor_power)
- Escape the e.g./i.e. lookbehinds: "machine." never ended a sentence
- Never cut a summary inside an open parenthesis
- Read and write UTF-8 explicitly so local Windows runs match CI
- TOOLS.md: add get_job_telemetry and list_audio_devices
The list now covers all 93 registered tools.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix: Generate the MCP tool list for every tool file and document telemetry tools
Clicking a machine or work coordinate on the Workspace page turns it into an input pre-filled with the current value. Enter (or clicking away) sends an absolute move on that axis only; Escape cancels. Clicking in and out, or retyping the shown value, sends nothing. Both columns move through the same work-frame G0 the axis dropdown's "Go To Work Zero" uses: a machine-column value is converted with the displayed origin offset (work = machine + offset), so the move lands where the table will then show the typed value, on every machine, without a G53 window. The Ray refuses targets outside its work area, as the jog pad does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Ray's GRBL firmware runs G0 at each axis's maximum rate and ignores F, so a position typed into the coordinate table crossed X far faster than the jog speed. Send G1 with the jog speed there, as the jog pad already does; other machines keep G0, whose F their firmware honours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Feature: Type a position into the Workspace coordinate table to go there
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No warranty expressed or implied. Use at your own risk. Ensure the safety of yourself and others and property at all times, following local laws and safety requirements, use Personal Protective Equipment, and an Enclosure for the CNC.
You must always be next to the machine ready to perform an emergency stop or switch the power off.