release: promote v0.18.0 to main - #419
Merged
Merged
Conversation
…phase 7) (#410) New pure `rs274.ts` interpreter for the RS274NGC programming layer: numbered/ named/global parameters (#100, #<name>, #<_global>), assignment (#n = <expr>), and bracket expressions ([...]) resolved wherever a numeric word value is expected (X[#1+2], Z#<depth>). Previously dropped → parametric programs rendered honest-but-empty; they now resolve to real geometry. Recursive-descent evaluator: RS274NGC operator/function set with correct precedence (** > */MOD > +- > compare > logical), degree trig, LinuxCNC MOD/EQ semantics, FIX/FUP/ROUND, indirect ##n and computed #[expr], read-only system-param allow-list (#5420-#5422 = position). Bounded + safe: a depth guard on ALL recursive paths (brackets, unary chains, indirect refs), no eval/Function, capped stores — a hostile program wastes only bounded CPU. Malformed/non-finite values drop the word with a specific disclosure, never throwing out of the parse. Additive: a `parametricProgram` capability (known/unavailable). Engaged only on lines using #/[ via a per-line scan — every FDM/simple-CNC line takes the untouched lexer, byte-identical (native goldens regenerated for the additive key only; ~2.7% parse-gate overhead measured on 3DBenchy, geometry unchanged). Scope: Phase 1 only. O-word control flow (if/while/sub/call) is a later phase. Reviewed (sub-agent) — fixed a stack-overflow DoS on unbounded recursion, a computed-NaN/uninitialized ambiguity, and silent non-finite drops before landing. Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…e families (scope only) (#411) Scopes (does NOT build) a future "provenance detection" epic: moving dialect detection from single-winner-per-kind to a multi-candidate, confidence-ranked model with an explicit ambiguity signal, validated against a labelled provenance corpus of the derivative slicer families (Orca/Bambu/Anycubic/QIDI lineage, Prusa/Super lineage) + the orthogonal controller axis. Recommendation: keep scoped until the corpus has real coverage and a concrete consumer need appears; reject speculative build + premature public candidates[] API. Non-G-code formats (Heidenhain .h, Siemens .mpf) out of scope. Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…e 7) (#413) Execute the RS274NGC O-word control structures CAM/LinuxCNC programs use to generate geometry: if/elseif/else/endif, while/endwhile, do/while, repeat/endrepeat, break/continue. A bolt circle written as a while loop, a pocket cut by a repeat, an if that selects a path — these previously ran once (or not at all) on the forward-only engine; they now resolve to the full, correct toolpath. New module rs274-flow.ts is the program-buffered interpreter of DD-017 D1: engaged only when programUsesOWords(text) (a cheap char-scan; FDM never enters it, byte-identical), it buffers the program, builds a block tree, and executes it — re-feeding each plain line through the same processLine (one geometry path) and evaluating conditions/counts against the shared Phase-1 parameter store (Rs274Context.evalExpression). Bounded and safe (this is now a small interpreter): new public maxProgramIterations limit (default 1_000_000) bounds TOTAL loop work — charged per loop pass AND per statement executed inside a loop body, so a large body cannot multiply work past the cap — stopping a runaway `o while [1]` with a partial IR and a rs274-iteration-limit disclosure. Structural nesting is capped; loops are iteration not recursion, so only bounded JS stack is used. No eval, no I/O. Honest and additive: clean flow keeps parametricProgram 'known'; a degraded run (hit cap / unbalanced / unsupported / malformed condition / geometry-limit truncation) reports 'approximated' with a specific disclosure. Subroutines (sub/call/return) are Phase 3 — disclosed unsupported and NOT executed inline. Non-parametric input never enters the interpreter and stays byte-identical (both golden suites unchanged). Streaming, which can't re-run a line range from a partial stream, discloses and runs linearly. Validated on a real LinuxCNC-idiom while-loop bolt circle (params + degree trig + flow). 31 new flow tests; detection pre-filter proven a strict superset of the classifier (no false-negative silent-wrong-geometry). maxCallDepth (subroutine recursion) deferred to Phase 3. Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(parser): RS274NGC subroutines (DD-017 Phase 3, #189 phase 7) Execute in-file O-word subroutines: o<id> sub/endsub, o<id> call [args], o<id> return. A call binds args to #1..#30 in a fresh call frame, runs the named sub's body, and returns; return exits early. Subs may be forward-referenced and may recurse. Correct, recursion-safe scoping (LinuxCNC): #1..#30 and #<local> are LOCAL to each call frame; #31+ and #<_global> are shared globals. A recursive sub's #1 cannot clobber its caller's; locals do not leak. The main program runs in the base scope, so a no-subroutine program is byte-identical (both golden suites unchanged). Bounded and safe: new maxCallDepth limit (default 50) caps recursion — an unbounded/mutually-recursive call is disclosed (rs274-call-depth) and skipped, never a stack overflow; a runtime exec-depth backstop bounds combined nesting+call-depth JS recursion. A sub called inside a loop still charges its body work against maxProgramIterations. break/continue/return are frame-local (never cross a call boundary). Honest: clean subroutine execution keeps parametricProgram 'known'; a degraded run (unknown sub, recursion-limit, duplicate definition, misplaced return) reports 'approximated' with a specific disclosure. Return VALUES are not yet propagated (documented Phase-3 limitation; return = early exit). External sub files (M98, o<name> call to a separate .ngc) remain an explicit non-goal. 44 subroutine + safety tests; full parser suite (243) + both goldens green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(parser): charge subroutine call-tree work; disclose endsub/arg mismatches (DD-017 Phase 3 review) Adversarial review found a bounded-execution hole: the iteration budget was charged only inside loops, so a loop-free recursive subroutine fan-out (`o100 sub` calling `o100` twice) spawned an exponential call-tree that maxCallDepth bounded only in DEPTH — total work was uncharged and, emitting no geometry, tripped no segment limit either → an effectively-infinite, uncancellable hang. Now every statement executed inside a loop OR a subroutine is charged, so total call-tree size is bounded by maxProgramIterations (disclosed rs274-iteration-limit). Verified with a fan-out regression test. Also (honesty nits from the review): an endsub whose id does not match its sub is now disclosed (rs274-unbalanced-oword), and a malformed trailing call argument is disclosed and dropped rather than silently truncated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(parser): update rs274-flow header — subroutines are supported (Phase 3) --------- Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…cope, custom bed (#414) * docs(design): DD-030 renderer/viewer interop — capture(), per-plate render scope, custom bed geometry Proposed design for the three product-approved generic renderer/viewer primitives, brought for public-surface sign-off before implementation: - capture() -> Blob on GcodePreviewControls (+ ModelViewer), render-to-target mechanism (no change to the interactive preserveDrawingBuffer default), inherited by all 4 adapters. - Per-plate render scope: a generic RenderScope on renderModelStill/ModelViewer with {plateId} sugar over an object-subset filter (model side only; toolpath plate isolation stays parse-time). Gated on capabilities.plates==='known'. - Custom bed geometry: additive BuildVolumeDef.shape (polygon/circular primitive, mesh reserved) + machineToVolume upgrade so discovered non-rect beds stop collapsing to a bounding rect. All additive -> one lockstep minor. Overhang/support-need viz explicitly deferred (no analysis substrate; own future DD). Sliced-gcode variant thumbnails + rectangular bed/texture confirmed already-shipped and out of scope. Consumer use-cases (AnyBridge) recorded as evidence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(design): DD-030 accepted — resolve §7 open questions (Blob-only, bbox cage, default non-rect bed, omit mesh) --------- Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add BuildVolumeDef.shape?: BedShape (rect|circular|polygon) so the build volume can draw an honest delta/round/irregular bed: a circle is polygonized, the outline is filled and drawn, and the floor grid is clipped to the outline (a round bed is no longer a square with a circle over it). machineToVolume() now maps a discovered circular/polygon MachineGeometry.bed onto shape, so discovered non-rect beds render as their true outline instead of collapsing to a bounding rectangle. The mesh escape hatch is reserved. Additive and byte-identical for rectangular beds (no shape / kind:'rect' takes the original path; grid/plate/cage/origin unchanged). Caller supplies the shape; the renderer just draws the polygon — no profile parsing, no vendor semantics. First increment of the DD-030 renderer/viewer interop batch. Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…D2) (#417) Add a generic RenderScope ({plateId} | {objectIds} | {instanceFilter}) and a pure, three-free applyRenderScope(scene, scope) that returns a filtered ModelScene (dropping non-matching objects/placements) with bounds recomputed from the kept placements, so framing follows the subset. {plateId} is sugar over the placement-level plateIds (DD-025). Wired into renderModelStill (renderScope option + folded into the still cacheKey so plate-1/plate-2 thumbnails key distinctly) and createModelViewer (initial renderScope + setRenderScope(scope|null) handle that rebuilds/reframes). Additive & honest: no renderScope renders the whole project unchanged; a {plateId} that matches nothing (no declared plate structure) renders empty by design (gate on capabilities.plates==='known'); declared plates/capabilities are preserved. Second increment of the DD-030 renderer/viewer interop batch. Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add capture(opts?) that returns the currently displayed view as an image Blob, for user-selected thumbnails, large-file fallback, and screenshots. Available on GcodePreviewControls.capture (all 4 adapters inherit it; the Web Component adds an imperative method) and ModelViewer.capture; implemented on InteractiveStage and delegated by ToolpathRenderer. Mechanism = render-to-target: render the current scene + active camera into an off-screen WebGLRenderTarget at the requested size and read it back, so it never flips the interactive preserveDrawingBuffer default nor disturbs the live view, and supports an arbitrary output size + independent/transparent background. The library returns the Blob and never triggers a download (caller owns the pixels, same contract as renderStill). Honest: capture() rejects with a typed CaptureUnsupportedError (E_CAPTURE_UNSUPPORTED) when the renderer cannot render-to-target (2D renderer, stub GL / no WebGL) or the stage is disposed/context-lost. Purely additive (new optional method on the renderer contract). Final increment of the DD-030 batch. Co-authored-by: Nathaniel Chestnut <sobechestnut-dev@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.
Promote v0.18.0 → main
Cuts v0.18.0 (14 lockstep
@chestnutlabs/*packages). Bundles two completed workstreams:maxProgramIterations/maxCallDepth), honesty-tiered, byte-identical for FDM/non-parametric input. Each phase adversarially reviewed.capture()→ Blob (feat(renderer): interactive view capture() → Blob (DD-030 D1) #418). All additive.Version PR #412 merged (c5d0bd4); release notes folded into
docs/README.mdcurrent-state + history (07fde26);docs:release-checkPASS.Public-docs completion check (CLAUDE.md)
Merge-commit (not squash), per the release process; then
gh release create v0.18.0 --target mainpublishes.