diff --git a/CLAUDE.md b/CLAUDE.md index 68fa937..f3b284a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,6 +94,76 @@ creation and the rename button can only rename a row to its current name; the dr send Alt, so a script cannot suppress inference. Still unmeasurable here: the RADV-to-lavapipe hot-pixel budget (no lavapipe ICD). +**M3 (sketching parity) landed 2026-09-07, in a serial prelude plus three parallel lanes.** +The sketch canvas went from eight tools to fourteen — `Select`, `Line`, `Circle`, `Arc`, +`Rectangle`, `Slot`, `Spline`, `Point`, and now `CentreRectangle`, `Polygon`, `ThreePointArc`, +`TangentArc`, `Trim`, `Split` — and every drawing gesture now COMMITS INFERRED CONSTRAINTS +(`vernier-solver`'s `infer_*`, at all four commit sites), with Alt suppressing, and with three +filters that are the difference between a guess and noise: never what the sketch states +verbatim, never a primitive's curve against another curve of the same primitive, and never one +whose trial solve makes the sketch `Redundant` — the solver's verdict decides, since a slot +stating `Parallel` itself makes a second `Horizontal` a duplicate no value comparison can see. +Sketch points and curves DRAG against the solver (a continuation drag with an applied echo, so +a locked rectangle does not move and a free point tracks). The **Project tool** pulls a face +centroid, an edge's ends or an edge's centre into the sketch as references, `ProjectEdgeEnds` +composing three commands behind one gesture; **construction geometry** is a per-curve flag with +a `┄` tile and a dashed drawing, refused as a profile by name; **trim and split** cut a curve at +a click, with the branch table's own refusals (a click on a curve's own end is refused rather +than silently duplicating the shared corner); **dimensions** gained arc radius, a `⌀ diameter` +reading, `RemoveConstraint`, and CLICK-THE-NUMBER — every `Distance`/`Radius`/`Angle` constraint +paints its value in the viewport and clicking it opens a card that edits it in place; **sketch +pattern** repeats picked curves linearly or circularly; and **construction planes** landed as +`SketchPlane::Offset` and `SketchPlane::Angled` (a hinge of two sketch points plus an angle), +with `RePlaneSketch` moving an existing sketch onto either. `SketchData.plane` has carried a +`SketchPlane` since 2026-09-02; what M3 adds is that a GESTURE chooses it — the base plane +buttons, the offset and angle steppers, and two re-plane rows — so the variants stopped being +document-layer-only. Document format **24**. The three repoints Lane C +wired (`SetLoftSections`, `SetSweepOrientation`, `SetSweepGuide`) were the last entries of +`reachability.rs`'s `NOT_YET`, so **the built-but-unreachable list is EMPTY for the first time +since it was written** and its expiry machinery (`Blocker`, the UI-enum reader, the prose pin) +is deleted; what remains is the guard — `SOURCES`, both directory walks, and `Role::Commands` +over `vernier-doc/src/command/`, which counts a command composed inside another command's +`apply()` as reached. `--selftest` holds **40 checks**, in lane order A · B · C (the report +digest moved once, deliberately; nothing stores it). +**The L2 gate is now DRIVEN, not only headless** (`scripts/drive/l2-enclosure.json` + its +negative control): the enclosure is built through the real shell from an EMPTY document — +centre rectangle, locked centre, typed dimensions, an inferred `Horizontal`, a trim, the +construction toggle, a sketch created on a FACE and re-planed onto an offset plane by the +ribbon's stepper, a CUT extrude through the `cut` chip, a linear pattern, and a dimension +edited by clicking its number, which propagates through the cut and all four instances. Its +volumes are analytic except the shelled body, which has no closed form and takes the oracle's +own plausibility band plus exact face and solid counts, with every later operation asserted as +an analytic delta from that export. +**Recorded M3 deviations.** A click within `DRAW_SNAP_MM` (2 mm) reuses the existing point, so +`Coincident` and `Concentric` are not what a drawing gesture demonstrates — the draw snap +subsumes the 0.5 mm inference tolerance, and two of lane A's own tests were passing on a +constraint that made the sketch `Redundant`. The drag echo is cleared on every refusal path in +`handle`, not in the arm, because a step the COMMAND accepts and the COMPILE refuses is rolled +back there. A cut click that lands on a point MARK (±1 mm of model space, arms reaching past +the corner) used to resolve to the point and be dropped in silence, so the refusal written for a +click on a corner was unreachable for exactly those clicks; `cut::curve_at` leaves the marks out +of a CUT pick only. The hover preview is answered as `Ask`/`Clear`/`Keep` with the armed tool +inside the question, because arming Select raises no pointer event and the trim cross went on +being painted under a tool that picks. Dimension labels are painted for exactly ONE sketch — the +one being drawn into, or the one selected in the timeline — because a label is an interactive +target that takes the click before the viewport sees it. The re-plane gestures ride ribbon +STEPPERS rather than value-card params, because `card_for` would float an offset field over an +empty viewport. There is no `show_datums` toggle yet, and a datum resolves a click only where +the ID buffer has no face. +**Four gaps the driven gate found, none of them fixed here.** (1) **A circular bore cannot be +cut through the GUI at all**: a cut extrude reads `ShellState::extrude_cut`, only the `cut` chip +writes it, the chip is painted only on the EXTRUDE card — and a picked circle floats the RADIUS +card instead, since `card_for` prefers a `Surface::Value` row. No selection puts the chip and a +circle on screen together, so the L2 gate's bolt holes are square pockets drawn as closed +chains. (2) **A trim leaves its old endpoint behind** as a free point of the sketch and no +gesture deletes a point, so reaching zero degrees of freedom costs a lock on geometry that draws +nothing. (3) **A dimension label takes a viewport click before the sketch does** (measured: a +click on one edge selected the neighbouring edge's label). (4) **The ribbon is one non-wrapping +row and overflows**: at 2400 px the sketch workspace clips its last four constraint tiles — lock, +unlock, construction, remove — so four committed M3 drive scripts are red at that width for no +other reason, the `$` command bar is the standing route to a clipped button, and the L2 gate +runs at 2600. Wrapping or collapsing the row is owed before the tool count grows again. + **M2-3/4/5 (booleans and seams) landed 2026-09-06.** Every production boolean runs `simplify: true` under Option C — a target face that absorbs tool faces of the same operation keeps its id and the absorbed tool faces are `Merged` into it; two target faces in diff --git a/FUSION_LOG.md b/FUSION_LOG.md index 917ce2b..14c9ff8 100644 --- a/FUSION_LOG.md +++ b/FUSION_LOG.md @@ -462,6 +462,16 @@ variant-addition should have carried. picking gesture: line-to-circle, circle-to-circle, arc-to-arc, and every mix. Nothing named by this entry remains open. +**Closed by M3 (2026-09-07): tangency is now PROPOSED as well as expressible, and there is a +tool that draws it.** `infer_arc_constraints` / `infer_circle_constraints` (SOLVER.md §5b) +offer tangent-to-line and tangent-to-circle on every drawing gesture, deciding the side and the +external/internal sense once and never re-deriving them, and `SketchTool::TangentArc` continues +a chain with an arc that leaves the previous curve tangentially — so the relation this entry +was about can be drawn rather than only constrained after the fact. Two caveats, both measured +rather than assumed: a proposal the sketch already IMPLIES is dropped by trial solve (a slot +states `Parallel` between its own lines, so a second `Horizontal` would make it `Redundant`), +and a tangency between two curves of the SAME primitive is never proposed at all. + ### 6. Sketch geometry's curve-editing tools don't reach every curve kind — offset, mirror-within-a-sketch, sketch-fillet, trim/extend and curve splitting are now all built. — **RESOLVED FOR WHAT THIS ENTRY ASKED** **Evidence this narrowed.** `SketchCurve` is four variants now (`Line`, `Arc`, `Circle`, @@ -578,6 +588,24 @@ trimming or extending a line or arc to a point, and splitting one into two are n ordinary sketch-canvas capabilities headlessly (trim/extend and splitting both pending their picking gesture). Nothing this entry named remains unbuilt. +**Closed by M3-4 (2026-09-07): trim and split have their picking gesture, and it is a TOOL +rather than a selection.** `SketchTool::Trim` and `SketchTool::Split` interpret an arbitrary +click near a curve — which is what this entry left open, and the reason it was left open was +that a click near a curve is not a selection of an entity. The armed tool answers the question +the click could not: under `Trim` the near half is kept and the far end moves to the click, +under `Split` the curve becomes two sharing one point. The branch table's refusals are reachable +and named: a click on a curve's OWN end refuses instead of minting a duplicate point at the +shared corner, and a split outside the span refuses. Both are driven through the real shell in +`scripts/drive/m3-b-trim-split.json`. + +**One residue this entry did not name, found by the L2 gate: a trim leaves its old endpoint +behind.** The far point is not deleted — it becomes a free point of the sketch, with two +degrees of freedom and nothing drawn at it — and NO GESTURE DELETES A POINT. So a trimmed sketch +cannot reach zero free degrees of freedom until someone locks geometry that draws nothing, which +is exactly what `scripts/drive/l2-enclosure.json` does before it can assert an empty warning +list. The missing primitive is "remove a sketch entity", and it belongs with the +curve-mutation work this entry already describes. + --- ## Rung L2 — blocks the jig and the enclosure @@ -648,6 +676,14 @@ names. (revolve, sweep, loft), control-point authoring, tangency and curvature-continuity constraints, and editing a spline's shape after the fact. +**Narrowed again by M3-11 (2026-09-07): a spline's fit points can be DRAGGED.** "Editing an +existing spline's point sequence" was this entry's last piece of the curve-mutation gap, and +half of it is now ordinary: a fit point drags against the solver like any other point, alone, +without dragging its neighbours (`sketch-drag`, and `scripts/drive/m3-a-drag.json` through the +real shell). What is still absent is changing the SEQUENCE — adding a fit point to an existing +spline, or removing one — which needs the same "remove a sketch entity" primitive entry 6's own +residue now names. + ### 10. Loft is ruled only, with no tangency and no guides. — **PARTIAL, INVESTIGATED 2026-08-29, NOT A FLAG TO FLIP** **Evidence.** `FeaturePayload::Loft` documents the choice explicitly and honestly: *"The @@ -836,6 +872,45 @@ wanted. And the m3 bracket's solved mirror plane — tilted ~1.5e-10 rad, enough a near-coincidence generator in its own right: the caps unify, the walls do not, and no linear tolerance changes that. +### 14. There are no variables and no expressions. Every dimension is a literal. — **DEFERRED TO M6, MEASURED** + +**What is absent.** A dimension is an `f64` in the document and nothing else: there is no named +parameter, no expression, no `= wall * 2`, no table of driving values. A part whose wall +thickness appears in nine dimensions is nine numbers to retype, and a design intent that says +"these two are always equal" can only be written as a constraint between geometry, never +between NUMBERS. + +**Why it is not in M3, and the measurement rather than the impression.** It was sized during +M3's planning and came out XL on its own: a `Value` that may be a literal or an expression +touches every dimension-carrying payload (extrude height, revolve angle, every +`SketchConstraint` that holds a number, fillet radius, shell thickness, draft angle, pattern +count and spacing, hole diameter and depth), needs an expression parser and an evaluation order +with a cycle guard, needs the solver to be handed the EVALUATED numbers while the document keeps +the unevaluated ones, and needs the UI to show both. It is also a second break of the save +format inside one milestone — M3 already spends version 24 on construction geometry and +`SketchPlane::Angled` — and the migration ladder M5-1 owes has not landed, so a second break +would be a second unmigrated file generation. + +**The decision (2026-09-06): M6, after the migration ladder.** Recorded here rather than left +as a hole in the list, because "no variables" is the single most visible difference between this +and any parametric CAD a person has used, and its absence should be a dated decision rather +than an oversight. + +### The click snap subsumes two inference rules (M3-1, 2026-09-07) + +`DRAW_SNAP_MM` is 2 mm and the inference distance tolerance is 0.5 mm, so a click near enough +to infer `Coincident` or `Concentric` has ALREADY reused the existing point: the two curves +share one point entity and the relation holds by identity, with nothing left to constrain. Both +rules remain reachable through a drag or a solve, and the hover glyph is unaffected. Two of M3 +lane A's own tests were asserting those rules through a gesture that cannot exercise them, and +were green on a constraint that made the sketch `Redundant`. + +Not a Fusion gap and not a defect — a tolerance relationship nobody chose. Whether the two +numbers should be RELATED (one derived from the other, or the snap stated in terms of the +inference tolerance) is an open question for whoever tunes snapping at dogfooding time; it is +recorded here so the answer is not re-derived from scratch the first time an inference looks +like it did nothing. + ## What this list is not It contains no assemblies, joints, drawings or multi-body work, and no CAM, simulation, diff --git a/SOLVER.md b/SOLVER.md index 15812fc..b03f91e 100644 --- a/SOLVER.md +++ b/SOLVER.md @@ -188,6 +188,40 @@ tolerances (2°, 0.5 mm). Inferred constraints reach saved documents, so inference falls under invariant #1; the UI's only role (Phase 5) is choosing whether to apply the proposals. +Extended (M3-1) to arcs and circles, as two more pure functions with the +same contract: `infer_arc_constraints` and `infer_circle_constraints`, in +fixed rule order — concentric (nearest earlier centre), endpoint +coincidence (arcs only), tangent-to-line (side decided once, never +re-derived), tangent-to-circle (external/internal decided once) — and +looking only at STRICTLY EARLIER entities, which is what makes a rule +stable when several curves are committed at once: an "all but self" +reading has a hexagon's edge 0 propose `Parallel{0,3}` and edge 3 propose +`Parallel{3,0}`, two different values naming one relation that no dedup by +value can see. Endpoint coincidence is the exception and is not restricted +that way, because a point id has no ordering relation to a curve id. +Equal-radius is deliberately NOT proposed: `SketchConstraint` has no such +kind, and the solver's `ArcRadiiEqual` is internal to `add_arc`. Ordering +is by rule then ascending entity id, and is asserted under entity +permutation, because inferred constraints reach saved documents +(invariant #1). + +The rules are a LIBRARY, not a policy: the app's commit path additionally +refuses to propose what the sketch states verbatim, refuses to relate a +primitive's curve to another curve of the same primitive, and drops any +proposal whose trial solve makes the sketch `Redundant`. + +**And the CLICK SNAP subsumes two of the rules.** `DRAW_SNAP_MM` is 2 mm +and the inference distance tolerance is 0.5 mm, so a click near enough to +infer `Coincident` or `Concentric` has already reused the existing point +and the relation holds by IDENTITY — there is nothing left to constrain. +Both rules stay reachable through a drag or a solve, and the hover glyph is +unaffected; what changes is what a drawing gesture can be said to +demonstrate. Two of M3 lane A's own tests were asserting those rules +through a gesture that cannot exercise them, and were green on a constraint +that made the sketch `Redundant`. Whether the two tolerances should be +related at all is an open question, recorded in FUSION_LOG rather than +answered here. + ## 6. Performance budget Gate: **< 5 ms** for a solve at ≥200 constraints, release build, on the