Repository navigation
fix(desktop): mechanisms print as their answers say, and a failed call leaves nothing behind - #358
Merged
Merged
Conversation
…ong one diagonally Audit D1/MG-03: width and depth were taken over all bodies together, so two 20 mm cubes designed 300 mm apart blocked as 320 x 20 mm. Audit D2/MG-02: only the axis-parallel box and its quarter turn were tried, so a 300 x 10 x 5 mm bar blocked on a 256 mm plate although it needs 219.2 mm a side across the diagonal. Every body is now measured as it lies and tried in steps of 0.1 degrees about Z; the finding names its body. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…body Audit D3: the area on the bed was added up over all bodies and compared once, so the 400 mm2 of a cube hid a sphere or a cone on its tip beside it (no contact finding). Each body now gets its own finding, named even when no face of it lies on the bed. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ross their whole face Audit D4, N-druckpruefung-1: the wall check knew no direction and no layer height. A 0.3 mm plate flat on the bed blocked as "thinner than the nozzle", and the 0.8 mm floor the design rule asks for warned as a wall under two lines. What lies within 45 degrees of flat, as the body lies, now stands against four layers (at least 0.8 mm) and blocks under one layer; the profile carries the layer height. Audit D6: only the middle of each triangle was measured, so a wall tapering from 2.5 to 0.2 mm passed. Rays are now spread a nozzle width apart and set at the corners, and the block counts the area under the nozzle, not the faces that touch it. Audit MG-04: a wall of exactly two lines (0.9 mm) was reported as under them. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…s corners rest on walls Audit MG-01, N-mechanik-1: a patch counted as a small overhang when every corner of its triangles lay within reach of an edge with material below. A flat face is two triangles with all corners on its walls, so the ceiling of a box (1600 mm2), a 40 mm bridge and the 102 mm ceiling of a guide's groove (3058 mm2) dropped out and the check read ready. Points spread over each triangle are tested now, not only its corners. A flat ceiling between two such edges up to 10 mm apart is a bridge and stays out (UltiMaker; the assistant's design rule takes the same constant); a wider one is reported. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Audit N-druckpruefung-2: SolidCraft's surface bodies (closed false, volume 0) ran through the check as printable parts: four walls without a floor gave only "0 mm2 on the bed", a cube with its top face removed only "bodies touch", and a mesh of five faces read ready. The render mesh keeps the count of mesh edges with one triangle, which the edge derivation already had. A body with any is reported as open, blocks, and is left out of the other checks. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… read as open Review of the print check: a degenerate triangle (a, a, b) left an edge from a to itself that no other triangle closes, so the count of open edges was above 0 and the check blocked a closed solid as an open shell. Such an edge is skipped. Measured on SolidCraft 0.40.1: a sphere (25504 triangles), a cone with a tip (5242) and a cube with a modelled M5 thread (8918) carry no such triangle and no open edge. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… no free edge between Review of the print check, three faults in what counts as a small overhang: A line through a point of a ceiling took the nearest anchor ahead and behind without asking what lay between. The roof of a blind T-slot (9 mm between the walls, a 3 mm slot down the middle) read as a 9 mm bridge: 0 mm2, where main had 189 mm2. A direction is a bridge only when the nearest rim of the patch on both sides is an anchor; the ring under a counterbore with a through hole is reported too (65 mm2). Any edge whose neighbour goes on downwards was an anchor. A shelf 8 mm out of a wall with a drip edge at its tip read ready, the lip counting as a second wall. An anchor now needs its material to come down, never up, to the bed or to a face that looks up; the shelf is reported with 240 mm2. The assistant was told that lying holes up to about 10 mm are bridged while the check warned from 8 mm. Both take LYING_HOLE = 8 mm now (MakerVerse, the one source of the audit's scale with a size): the roof of a lying hole up to that stays out. The cases stand on closed bodies (a grid of blocks, prisms with clipped ears). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… without a word Review of the print check: A body whose model carries no count of open edges fell out of both the open and the closed bodies: 10 x 10 x 300 mm read ready. It is checked as closed. A profile without a layer height switched the floor check off (a 0.3 mm plate read ready). It is checked as 0.2 mm. "(four layers)" was wrong under a 0.2 mm layer, where the 0.8 mm minimum is more (6.7 layers at 0.12 mm). The finding carries the number of layers and the texts name it. Fifty pins made fifty contact findings, of which the report kept 40 and the note 12. The bodies with little on the bed are one finding with their count; the report says how many findings it leaves out, and the note how many findings and bodies. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ck, not left under "not built" A joint SolidCraft refused, or a guide whose travel could not be measured, left the knuckles and cuts in the design while the answer said "Not built"; the next call built them again (audit 2026-11-12, H-02: 2 -> 11 -> 20 features). Every step since the undo depth read before the build is now undone in one batch, and the answer names SolidCraft's failed command and its reason instead of its raw JSON. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…t twice between two bodies SolidCraft takes a joint name twice; sweep and limits by that name then met the first joint (audit 2026-11-12, N-mechanik-3: the answer gave the first hinge's limits, the second had limits null, a third call built a third joint between the same parts). Measured after: second joint "Hinge (2)" with limits [-67, 145], the repeated call refused with the design unchanged. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ille The finger notch was cut after the bead and took away the part of it past the wall's plane: the bead ended on the plane and caught nothing, in the tool and in the hinged box template (audit 2026-11-12, N-mechanik-2). The notch is now cut first. Measured: bead reach past the wall 0.000 -> 0.250 mm (walls 1.6, 2 and 10 mm, rooms 20x12 and 56x36, radius 0, 2 and 8, the template), flat lid 0.250 mm as before, no interference. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ys how the parts print The groove's ceiling was level and 13.9 mm wide at the defaults (up to 102 mm), a gap over the rail's head, while the tool called every mechanism "printed in one piece without support" (audit 2026-11-12, T5 and N-mechanik-6). Where the moving body stands high enough over the rail's face the ceiling is now a roof at 45° with a ridge of 5 mm; where not, it stays flat and the answer says so, with how to lay the part. The two parts print apart, which the description and the answer now say. Measured with the print check of this branch, PLA 0.4: default guide warn 418 mm² -> ready, dovetail to plug warn 389 mm² -> ready; gap, travel and body count as before. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…all that is there The answer said knuckles above the bed "stand on webs at 45°, so they print without support", and the print check still found 90 mm² (barrel 6), 121 mm² (8) and 619 mm² (40), up to 89.5° (audit 2026-11-12, N-mechanik-4): each web ended a gap before the other body, so the far half of every barrel hung, and so did the pin inside the moving knuckles. A web was also drawn to the wall's plane where the wall had ended, 4.64 mm in the air under a lid plate (N-mechanik-5). - The web starts where the barrel leans 45° on the far side; the other body is cut back by the same profile a gap further out. - A second trial rod, inside each wall just under the web's foot, says whether the wall reaches down there: a body without it gets no web, and the answer names it. - The pin is cut flat underneath inside each moving knuckle: a level bridge from one fixed knuckle to the next. The default knuckle count of a lying hinge keeps that bridge at 10 mm or less (30 mm of line: 5 knuckles, was 3). - The answer says "without support" only where every knuckle stands on a web and the bridge is within 10 mm; otherwise what is left. Measured, print check of this branch, PLA 0.4: hinge on 16 mm blocks warn 121 mm² -> ready (barrel 6: 90 -> ready, 40: 619 -> ready), on the template's shells 174 -> ready; lid plate in the air: no web under it, said so. Gap 0.400 mm, no shared volume, one shell per body, limits as before. The template keeps its joint on the pin's round top and draws every profile at its own place. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…l's webs The template built the hinge without the webs of the mechanism tool: its knuckles began 12 mm above the bed in the air, under a file that says every template prints without support (audit 2026-11-12, T3). It now names the walls of its two halves and the bed, as the tool measures them, and the webs follow length and height like the rest of the hinge. Measured, print check of this branch, PLA 0.4: warn, overhang 189 mm² up to 87° -> ready; after length 80, height 24: no overhang finding either. Gap 0.400 mm, no shared volume, one shell per body, and the lid closes to the bead's snap at 175.6° as before. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…part lies, in one pass Second review of the print check. What counts as a small overhang is reworked: The bridge was tried in twelve fixed directions. An 8 mm tunnel turned by 1 degree was reported whole (240 mm2), a lying hole of 8 mm was free at 0 and 90 degrees only, and a roof meshed as a grid was reported at 15 degrees. The directions now come from the anchors of the patch, and one is chosen for the patch as a slicer lays a bridge. Points just inside the corners are tested, not points on the rim. A hole in a bridge reported the whole ceiling (231 mm2 of 30 mm, 791 mm2 of 100 mm). Only what does not hold is reported, with the arm back to its anchor: the two stubs beside the hole, 15 mm2. By the same rule the roof of the blind T-slot reports its two arms (162 mm2, not the 9 mm bridge behind the slot) and the ring under a counterbore its two stubs (20 mm2). Whether material stands was searched anew from every rim: 14 s on 24000 triangles of a stepped funnel, 56 s on 48000. One flood from the bed finds it, upwards only and across overhang no further than a small one reaches; 100000 triangles take under a second. A face that looks up stands when the underside straight below it does, so a lip with a hook, a step or a bead that looks up no longer carries a shelf. A hole of 9 mm with 20 segments stayed out: the rim of its roof lies on a mesh edge. The distance now runs to where the curve passes the limit angle. A profile without an overhang angle is checked at 45 degrees. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… an angle or with many bodies Second review of the print check: the overhang finding carried the profile's angle as it came, so one that is not a number failed the report's schema; it carries the limit that was checked. The count of bodies with little on the bed was capped at 500 in the schema, where a larger design would have lost the whole report. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ds, not by the undo depth SolidCraft's undo depth stops at 200 (0.40.1: 260 steps read undo 200). The difference of two depths then comes out too small or 0: with 170 steps before, 30 of a hinge's 54 steps were undone, with 200 none, both under "Everything was taken back" (review of PR #358). takeBack() keeps features, sketches, bodies and joints as the mark and undoes until the design reads like it again, never below the mark's number of features and never more than 400 steps; it says false where that cannot be reached, and always makes the design level active again, since activating a component is no undo step. A function of its own over run(commands), so the block tool can use it too. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…, and promise by material From the review of PR #358, each measured on SolidCraft 0.40.1: - The groove's roof went by the moving body's box: a plate 6 mm thick with a knob on it got the roof (ridge at 7.9 mm) and was cut in two along the rail, under "Neither needs support". Rods along the groove, over the ridge and both flanks, now say whether the body is solid there (plate with knob, drawer with a 5 mm floor: flat ceiling, two bodies, said so; block and slanted block: roof). - Any step that leaves a new body behind (created_bodies, no warning) fails the call, which is taken back, for every kind of mechanism. - A failed call is taken back with takeBack(): past the ceiling of the undo depth too (187 features before, a hinge, joint refused: 187 after, twice). - A second hinge on the line of the first is built and shares the first joint, which is swept again (120 mm flap: one joint, no conflict, one degree of freedom); only a hinge on another line is refused. At nine knuckles the advice is two shorter hinges on one line. - "Without support" is said only for a material that prints 45°; for one that stops at 40° the answer names what leans 45° (hinge, guide, dovetail, screw lid, latch). The angle comes from the part's print profile, like the nozzle. - A flat groove ceiling within the bridge span of the print check (2 x 1.5 mm rail: 6.9 mm) is called a bridge that prints, not "turn it over". - The hinged box's webs end on the bed at the latest (height 5 mm: lowest point -1.16 mm -> 0); the description names no height that holds for one nozzle only; the refusal ends with a full stop. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… is not built twice Second review of PR #358. - The two flank rods lay in the groove beside the ridge, not over the middle of each flank: a plate with a rib over the rail got a roof that cut it along both sides of the rib. The test now fixes where all five rods lie. - A second hinge call on the stretch of the first is refused again (H-02). - A rod may lie outside the body by its own width at the most, not by a tenth of the groove; a trial that failed or went by the box of a body on a line askew to the axes is said as that, not as "it is not solid". - A shared joint whose sweep fails keeps its old limits, and the answer says so. - The message for a body cut apart no longer adds where to put the line. - Hinged box: a web cut short by the bed keeps its face at 45°. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…roof trial says so Review 3 of PR #358. - A hinge that reached only partly over the first, or over a second one without a joint of its own, was built into the old knuckles: only the first joint's point was tried. The pins on the line ("<name> ●", read with timeline.inspect) now say where hinges are. - A rod or the comparison that failed in the roof trial read as rods outside the body ("is not solid"); it is a trial that failed. - A body with round or chamfered ends is full over the groove and still gets no roof; the answer now says where material is missing instead of "is not solid". - Measured on SolidCraft 0.40.1: the overlap volume of a rod is exact against the analytic volume at 30, 100 and 300 mm, so the roof's threshold stays as it is. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…rned and mirrored 178 cases, each in nine turns on the bed and mirrored. The 27 the check still gets wrong run as expected failures (ten overhangs that pass as printable: low stairs, a drip edge with a cheek; seventeen printable shapes that are reported: channels that turn, tunnels with cut or rounded corners, lying nut pockets). The check itself is unchanged. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…wth, not in seconds The thread took 3.9 s against a limit of 3 s on a busy runner. Each mesh is now timed against one a quarter of its size, the fastest of three runs each: one pass takes 4 times as long (measured 4.2 to 7.3 under load), searching anew from every rim 16 times; the limit is 12. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… click Two contract runs on unrelated branches failed here with every click missing the text although the clicks arrived within milliseconds. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
ManuelStaggl
force-pushed
the
fix/mechanism-audit
branch
from
November 12, 2026 16:42
b6d8269 to
a9cd846
Compare
…uilding Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
# Conflicts: # apps/desktop/src/main/index.ts
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.
Symptom
The contract test "a slot built into a hinged flap stays in it when the flap turns (ADR 0027)" fails in about half of its runs: the log says "built the function block slot", SolidCraft has cut the slot (volume 4855.69 → 4683.37 mm³, faces 18 → 22), but the view stays at "2 bodies · 35 faces" until some later change. Print check and export preview stay on the old design with it.
Cause
HeadlessEngine.checkForChange(quiet)always recorded the revision it read and told the listeners only when it was not quiet and the revision had moved since the last check of either kind.applyBlock): it waits for running trials once, then reads sketch names, the component, and runsapplySteps.applyStepsand the change's own check; its quiet check records the revision the block made.The same holds for the autosave (
saveends with a quiet check) and for tool calls of the assistant, whose check incallTool'sfinallycan be overtaken by a quiet one in the same way.Fix: bookkeeping in the engine (way b), not one queue for everything (way a)
The engine keeps apart what the revision alone cannot tell, at the one place where it is recorded (
HeadlessEngine.check):changed(): after a change PartBuddy made itself (the nine places in the supervisor). The listeners are told even when a quiet check recorded the revision first.MirrorScheduleralready drops a revision it has delivered, so a change that left the revision alone costs nothing.unchanged(): after a trial or a save. Tells nobody. When it records a moved revision while a tool call of the assistant is under way, it leaves a note, and that call's own check tells the listeners.checkForChangeis gone from the public surface, so the supervisor cannot ask for the ambiguous "maybe" check any more; onlycallTooluses it.Why not the queue: it would put every tool call of the assistant, every trial and every autosave behind one another, a change in timing for the whole product that cannot be tried here without the app, with self-blocking to guard against (
applyBlockruns a trial for the bold font before it builds). And it holds only as long as every caller remembers to queue; the autosave does not go throughprobingtoday. The bookkeeping never makes anything wait and can only tell more often than before, never less.Known cost: a read of the assistant during which a trial ends is mirrored once more than needed, because the engine cannot know that the call only read. A trial alone, and a read after it, still mirror nothing.
Not changed: trials still run interleaved with tool calls of the assistant. Each trial is one batch that SolidCraft takes back itself, except the snap-fit test of a joint sweep, which reads where the joint stands and puts it back in a second batch; a
joint.driveof the assistant between the two would be taken back. That needs the queue and is left for its own change.The window mode is not affected: there SolidCraft reports each change itself with
design.changed, and PartBuddy keeps no revision of its own that a quiet check could move.A line in the log ("revision N was recorded before its change was told; telling it now") shows when the guard was needed.
Tests
apps/desktop/test/headless.test.ts, against a stand-in whose revision the test moves by hand (the first two fail without the fix):typecheck, eslint and
vitest run(721 passed) are green.After the review (commits 29786b8, 6623081)
created_bodies: ["B (1)"], answer "Neither needs support"readycreated_bodies/removed_bodiesfails the call, which is taken back (all kinds)takeBack.ts: undoes until features, sketches, bodies and joints read as before. 187 features, hinge with webs, joint refused: 187 features after, twice; design level activeconflicts: [], 1 degree of freedom, limits [-126, 67]; a hinge 0.5 mm off the line is refused@partbuddy/geometry)Geometry is still 45° for every material: the answer is bound to the angle, the webs and the roof are not tilted.
The scenes of both mechanism cases are as before these two commits (the same 22 of 100 differ from the baseline; no shared volume, no open body, body counts unchanged).
Not done: the insert seat of a screw joint too near an edge (N-mechanik-8) breaks out without making a new body, so the new check does not see it; it needs a measurement of its own.
The texts under "What the assistant is told now" are as of the first round; where the review changed them, the table above has the new wording (the guide no longer names a height in millimetres the body "stands", the refusal of a second hinge applies to another line only).
🤖 Generated with Claude Code
Round after the second review
(ceiling + ridge) / 4from the line (4.73 mm at the defaults), whereslopeis reckoned. A plate with a rib over the rail gets the flat ceiling instead of a roof that cuts it. Test:mechanism.test.ts, guide test,high.probes(all five rods by place and length; checked outside the fake, since a throw inside the trial reads as a failed trial) and the caserib. Tried in the SolidCraft CLI on the reviewer's two models (groove through, groove ending in the body): rods over the ridge 1, 1, 1, over the flanks 0, 0, so no roof; the flat groove makes no new body and leaves 2.6 mm of plate beside the rib.from–to, lies within it: "Change that one instead". Test: same test as the coaxial hinge, caseover(knuckles: 7on the first hinge's line, nothing built).failedandaskew. Not done: laying the rods by the body itself instead of its box, so over an askew line (or a body turned on its face) the groove still gets no roof; ashortcut:comment says so.hole: 1.5.unrun.cut.min(0.5 mm, height - 5.657 mm)), so the face keeps 45°. Test:templates.test.ts(Hinge 1 ◣,Hinge 2 ◣); the sketch tried in the SolidCraft CLI atheight = 5 mm: 2.17 mm across, 2.17 mm up.Run: eslint on the four files,
pnpm run typecheck,vitest run test/mechanism.test.ts test/templates.test.ts(46 tests). Not run: the full suite, Playwright, the contract tests.Review 3 (
8c8e1d2)S1, a hinge partly over another: besides the first joint's point, the pins on the line say where hinges are. Every hinge keeps its pin as the feature
<name> ●;timeline.inspectgives its base, axis and height (checked on SolidCraft 0.40.1). A stretch that overlaps a pin is refused, with that hinge's name. No new store. Tests: y 16–46 over the first (0–30) refused, y 60–90 over a second one without a joint (40–70) refused as "Hinge (2)", y 90–120 beside both built. Limit: a pin that was renamed is not found; then only the joint's point is tried, as before.S3, measured, no change to the threshold:
inspect.clearancegives the overlap of a rod exactly, not faceted or rounded. Full box carriage, five rods Ø 0.2 mm wholly in the body, SolidCraft CLI 0.40.1:The relative error at 300 mm is 4e-10. The analytic volume stays the comparison.
S2, ends that are not flat: measured on an upright cylinder Ø 30: the rods beside the middle come 0.42 mm and 1.53 mm short, so no roof. Not changed (shortcut noted in the code); the answer now says it: "… it is along most of it only (material is missing at that height along up to 1.5 mm of it: at ends that are round, chamfered or turned, or in a gap across the groove)" instead of "it is not". Said when every rod lies at least half in the body. Test: case
hole: 1.5.K1, a trial step that fails: a batch that ends before the comparison is a trial that failed ("could not be tried"), not rods outside the body. Test: case
refused. For the webs of a hinge the same failure leaves no web, as before.Run: eslint on the two files,
tsc6 -p tsconfig.node.json,vitest run test/mechanism.test.ts(36 tests). Not run: the full suite, Playwright, the contract tests; S1 was not tried end to end in SolidCraft (only the shape oftimeline.inspect's answer), nor with the hinge's bodies inside a moved component.