Repository navigation
fix(geometry): the print check is right about size, contact, walls, bridges and open bodies - #353
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>
ManuelStaggl
force-pushed
the
fix/print-check-audit
branch
from
November 12, 2026 13:06
aa04bc7 to
e294d82
Compare
…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>
…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>
ManuelStaggl
added a commit
that referenced
this pull request
Nov 12, 2026
…ks measure before they cut (#357) A-findings of the full audit (2026-11-12) on the rules for the assistant round the print check, on top of #353, plus the report of 2026-11-12 that the assistant does not optimise print time and material on its own, and the points of the independent review of this PR. `origin/main` is merged in (undo pattern of #348, block refusals of #355). ## R1 – the check reaches the assistant in the turn that changed the design **Before:** "Read them after your last change, before you call the part done" was a request. The check reached the model with the next user message; a turn could end on status `block` it never saw. **After:** the work log (`workLog.ts`) reads SolidCraft's history before and after every changing call. When the design changed, it hands the tool's answer to `withCheckOfChange` (`printCheckNote.ts`), which waits until the mirror has delivered that revision and the renderer has reported its check, and puts the note behind the answer. It waits outside the work log's queue. **Why this and not a step at the end of the turn:** Claude Code (gate proxy), ChatGPT and local models (`loop.ts`) all call tools through the one chain `track(upstream)` in `index.ts`. One hook there covers all three; a turn-end step would have needed three implementations and a guard against loops. A tool answer cannot loop: no change, no note. Details, after the review: - **Two stages.** With two bodies or a joint the renderer sends the mesh check at once (`measuring: true`) and the full check when SolidCraft has measured clearance and joints. The answer waits up to 1.5 s for the full check, then takes the mesh check marked as preliminary; after 3 s without any check it says so. Timings at SolidCraft 0.40.1 without a window (median of 5): mirror and mesh check 211 ms for a box with its lid, 141 ms for a hinge; the hinge's two joint sweeps 498 ms; `inspect.clearance` 1 ms for both; the renderer adds 250 ms of quiet time. Not measured: the renderer's own check time in the app. - **What counts as a change:** the undo depth moved, or a change replaced what Redo held (`designChanged`). The revision alone does not: trials, saves and a block that was taken back move it. At the cap of SolidCraft's undo history (200; a step there leaves the depth and moves the revision) a moved revision counts, at the price of a check too many after a trial. Measured: a probe batch that is rolled back leaves revision, undo and redo as they were; `body.print_face` moves revision and undo depth, so it gets its check. - **Told once.** `CheckTold` gives the full note the first time in a turn and whenever status, any finding (all of them, with faces and numbers, not only the lines shown), the preliminary mark or a failed measure change; else one line plus the measures that changed. A new user message starts over. - **Changing tools** now include `partbuddy_place_drawing` and `partbuddy_move_drawing` (check, queue, lock while a question is open, a line in the work log). The other PartBuddy tools (`view`, `standard`, `ask`, `plan`, `assembly`, `design_rules`) change no geometry. - When the history cannot be read, the answer carries no check and the log says so. Texts to the assistant: - VIEW_GUIDANCE, replacing the request: `PartBuddy checks the part for printing after every change and shows the result to the user as a light; the findings come behind the answer of each tool call that changed the design, and partbuddy_view gives the latest ones.` - Pending behind a tool answer: `[PartBuddy: the ready-to-print check of the latest change is still running; call partbuddy_view for it before you call the part done.]` - Pending from `partbuddy_view` itself: `[PartBuddy: the ready-to-print check of the latest change is still running; it will be here in a moment.]` - Preliminary: `Preliminary: clearance and joints still being measured (bodies that overlap or come closer than a push fit, joints that stop before a limit); call partbuddy_view for the full check before you call the part done.` A clean preliminary check reads `nothing found so far (build volume, wall and floor thickness, overhang angle, area on the bed, open bodies, failed features, bodies that touch).` - Preliminary from `partbuddy_view` itself: `Preliminary: clearance and joints still being measured (…); the full check will be here in a moment.` - A measure that failed: `NOT measured, because SolidCraft's measure failed: bodies that overlap or come closer than a push fit. Measure it yourself (inspect.clearance) before you call the part done.` The clean line then leaves that part out of what it checked. - Unchanged while measuring: `[PartBuddy: ready-to-print check after this change: unchanged, nothing found; clearance and joints still being measured: call partbuddy_view for the full check before you call the part done.]` - Unchanged: `[PartBuddy: ready-to-print check after this change: unchanged, still not ready to print, the same 1 finding as before.]` / `… unchanged, nothing found.]`, followed by `Measures that changed:` and those lines when there are any. ## R3 – a clean check says what it looked at **Before:** `ready to print, nothing found.` / `The print check in partbuddy_view still judges what you build.` **After** (from the finding kinds of `printCheck.ts`, `withClearance`, `withMotion`): - clean: `… nothing found in what it checks (build volume, wall and floor thickness, overhang angle, area on the bed, open bodies, failed features, bodies that touch, bodies that overlap or come closer than a push fit, joints that stop before a limit).` and at the end `Not checked: strength and layer direction under load, the fits and hole allowances you chose, details and text too fine to print, bed-edge chamfers. Judge those yourself.` - with findings the last line ends with the same `Not checked: …` sentence. - standard topics: `None of this has been printed by PartBuddy yet, and the print check does not test these rules: measure what you built against them.` ## R54 – blocks measure the plastic they need One measure for all four: a probe cylinder is laid in as a body in a batch that is rolled back, and `inspect.interference` says how much of it lies in the part. The cylinder starts 0.5 mm under the point, so a face that falls away that little (a curved one) is not read as a gap, and an edge or a chamfer is. | Block | Needs | Measured | |---|---|---| | keyhole | entry wide, lip + pocket + 1.2 mm deep, behind the entry and behind the screw's rest | before anything is cut | | magnet | pocket + 1.2 mm round it + 0.6 mm under it | before anything is cut | | insertSeat | the maker's wall round it (at least 2 mm, as a boss) + 0.6 mm under it; and the seat cuts its whole cylinder | after the cut, each hole along the way it went in (read from what that hole took out of the body: `solid.hole` names no direction); taken back when short | | woodScrew | the screw's diameter of plastic round its clearance hole (4 mm screw: 12.7 mm wide, so a 15 mm tab takes it) + 0.6 mm of that hole under the countersink | the same, without the volume check (the plate's thickness is not given) | | any cutting block | the body stays one body | after the cut; taken back | `0.6 mm` under a seat and under a countersink are our own choice (three layers), marked so in the code. "Round its hole" for the wood screw is the reading of the block's own sentence ("between a hole and an edge"); measured from the countersink it refused a 15 mm tab. Refusals, as the engine gave them: - `Not built: at [220, 30, 0] P3 has 3 mm of plastic where 6.7 mm are needed (the mean over a width of 9 mm; the far side, an edge or another cut takes the rest); a keyhole needs the part 6.7 mm thick behind its entry and its slot. Thicken the part there, move the keyhole away from edges and other cuts, or use woodScrew.` (an entry over the edge names the entry: `at [30, -3, 0] … 1.18 mm`) - magnet: `… has 3 mm of plastic where 3.3 mm are needed (the mean over a width of 8.7 mm; …); a pocket needs 1.2 mm of plastic round it and 0.6 mm under it. Thicken the part there, move the pocket away from edges and other cuts, or take a thinner magnet.` - insertSeat, the probe: `… has 6.52 mm of plastic where 7.3 mm are needed (the mean over a width of 8 mm; …); the seat of an M3 insert needs 2 mm of plastic round it and 0.6 mm under it. Thicken the part there, move the seat away from edges and other cuts, or use a boss.` - insertSeat, the volume: `Not built: at [4, 20, 20] the seat would cut 83.2 mm³ of plastic where its whole cylinder is 84.2 mm³: its rim breaks out of the face, into an edge, a chamfer, the far side or another cut; the seat of an M3 insert needs …` - woodScrew: `… has 2.49 mm of plastic where 2.65 mm are needed (the mean over a width of 12.7 mm; …); a countersunk hole for a 4 mm wood screw needs 4 mm of plastic round its 4.7 mm hole and a plate at least 2.65 mm thick. Thicken the plate, or move the hole away from edges and other cuts.` - split: `Not built: it would leave 2 bodies where Strip was one (Strip (1)): the cut goes right through the part there. Give a point with more plastic round it, or a smaller size` - not taken back: `…. What was built of it could NOT be taken back: undo it before you call again` (and a line in the log) Success answers end with `PartBuddy measured that plastic before building, from 0.5 mm under the face on: it is there.` (keyhole, magnet), `PartBuddy measured that plastic, from 0.5 mm under the face on, and that each seat cut its whole cylinder: it is there.` (insertSeat), `PartBuddy measured that plastic, from 0.5 mm under the face on: it is there.` (woodScrew), or `PartBuddy could not measure that plastic this time: check it yourself.` insertSeat adds `Each seat needs 2 mm of plastic round it and 0.6 mm under it.` **Taking a block back.** SolidCraft 0.40.1 reads the depth of its undo history as 200 at most (211 features: 200; one more: still 200, revision and feature count move). A block that must not stay is therefore undone one step at a time until the design measures as before (same bodies, same volumes, same number of features and sketches, history no deeper), with at most the steps it sent plus three; then the top component is made active again. **At the engine** (SolidCraft 0.40.1, audit harness): - The shared volume is exact, not facetted: seven probe cylinders wholly inside a block, a rod and a ball, along the axes and askew, each gave 1.0000000 of their volume; with 0.02 mm of 2.8 mm outside 0.99286; with 0.01 mm of the width over an edge 0.99993. The threshold stays at 0.999. - The centre of mass comes with all the digits of a double: on a block of 200 × 200 × 50 mm the axis of one M3 seat was 0.000007° off. On a 45° face it read `[-0.707107, 0, -0.707107]`. - Built, with the measured sentence: keyhole in 8 mm, magnet in 4 mm, insert M3 in 10 mm, wood screw in 8 mm; magnet and two insert seats on a 45° face; magnet and insert seat on a rod of 20 mm radius (the seat took 0.03 % less than its cylinder there); two wood screws on a stepped plate (8 and 4 mm); a 4 mm wood screw in a 15 mm tab. - Refused, nothing left behind: keyhole in 3 mm and with its entry over the edge; magnet with 0.3 mm under it and 1 mm from a wall; insert seat in 3 mm, in a plate exactly as thick as the seat is deep, 2.5 mm from an edge, on a 3 mm pilot hole through the part, 4 mm from an edge with a 3 mm chamfer, and one of two over a pocket from below; wood screw 5 in a 3 mm plate and 5 mm from an edge. - A probe batch leaves revision, undo and redo untouched. - The harness's `bloecke` cases (40) pass. Limits: what is missing must be more than a thousandth of the stock's volume to be seen (a seat 6 mm from the edge with the 3 mm chamfer is built: its wall is 1 to 2 mm over the top millimetre only). The uppermost 0.5 mm are not probed. Pockets of one call are measured against the body before the cuts, not against each other. The same conditions in `mechanismTool.ts` are untouched (others work there). ## Material and print time (reported 2026-11-12) **Rule group `economy`** (fifth group, on by default; settings switch "Design lean" / "Sparsam konstruieren", `partbuddy_design_rules { economy }`). Text for a 0.4 mm nozzle and 0.2 mm layers: > - Material and print time, from the first shape on and not as a clean-up: build a shell, not a block. Slicers print 0.9, 1.35, 1.8 mm (two to four lines) as wall and fill anything thicker with sparse infill, so stiffen with ribs as thick as the wall, at least two lines (0.9 mm), instead of thickening the wall. Keep the part low as it prints: every 0.2 mm of height is one more layer, and print time grows with the layers. Shape it so it needs no support, which costs more time and material than any other choice. Leave out whole regions that do nothing (an open frame instead of a plate, a shorter body); small pockets and holes in a filled region add walls and save little or nothing. Split a part only when that saves support or much height and a joint is acceptable. Loaded regions, sizes the user names and what the user wants solid stay as they are. Infill, speed and layer height are set in the slicer: never promise grams or minutes. The print check lists each body's solid volume, height, layers and support area: compare them before and after a change. The report line becomes `After each design step, name what you added on your own, what you did for material and print time and the sizes you snapped in one short line, e.g. "…, shell 1.35 mm with two ribs instead of a solid block"`. **Measures with every check** (tool answers, next message, `partbuddy_view`), computed in the renderer from the mirrored mesh with `meshMetrics` and `overhangOf`: ``` Per body as it prints. Solid volume overstates what thinning a thick region saves, because the slicer fills it sparsely anyway; the shell estimate (two wall lines on every surface, 15 % infill inside: an assumption, not the slicer's settings) is nearer to the filament. What counts for sure: fewer layers, less area to support, less surface. Promise neither grams nor minutes: - "Box": 6 cm³ solid, about 2.58 cm³ as a shell, 10 mm high (50 layers), 2200 mm² of surface, 600 mm² on the bed, 0 mm² leaning past the overhang limit. - "Pin": 90 mm³ solid, about 90 mm³ as a shell, 10 mm high (50 layers), … - and 3 more bodies. ``` The shell estimate: surface × two lines, never more than the solid, plus 15 % of the rest. A block 40 × 40 × 20 mm reads 32 cm³ solid and about 9.7 cm³ as a shell; a wall of four lines reads the same in both. Six bodies are listed, the rest counted; volumes under 1 cm³ are given in mm³. No mass: the material profiles carry no density. A body that cannot be measured is left out and named in the log; a report the main process refuses is sent again without measures. `packages/geometry` is unchanged; `laid` (print face down) and `smallOverhangReach` are not exported there, so `bodyMeasures.ts` repeats the pose and the reach (marked `shortcut:`). ### Yardstick | Topic | Rule with number | Source 1 | Source 2 | Contradiction | |---|---|---|---|---| | Walls as lines | Whole multiples of the line width; 0.4 nozzle: 0.45/0.9/1.35/1.8 mm for 1–4 perimeters; strength mostly from perimeters; shells typically 0.8 mm | Prusa, https://help.prusa3d.com/en/article/modeling-with-3d-printing-in-mind_164135/ , https://help.prusa3d.com/en/article/layers-and-perimeters_1748 | Protolabs Network, https://www.hubs.com/knowledge-base/selecting-optimal-shell-and-infill-parameters-fdm-3d-printing/ | Minimum wall differs (Hubs 0.8 mm, UltiMaker 0.5 mm, Stratasys Direct 4 × layer). No source names a thickness above which more wall stops paying off, so the rule names none. | | Ribs instead of thick walls | Ribs stiffen thin walls and large flat areas; in FDM a rib needs no thinning. Rule: as thick as the wall, at least two lines | Stratasys Direct FDM guide (p. 4, 7), https://www.stratasys.com/siteassets/sdm/content---website-storage/design-guides/dg_sdm_fdm_0725a.pdf | Protolabs Network, https://www.hubs.com/knowledge-base/dfm-tips-for-3d-printed-parts-with-thin-walls/ | Yes: Hubs gives 50–75 % of the wall, the rule of injection moulding against sink marks; on a 0.9 mm wall that is one line, under the thinnest wall that prints. Not taken over. Stratasys Direct also says solid can build faster than hollow with ribs. | | Height in Z | Print time follows the layer count: a 10 × 30 mm cylinder is about 100 layers lying, 300 standing (0.1 mm) | Protolabs Network, https://www.hubs.com/knowledge-base/how-does-part-orientation-affect-3d-print/ | Xometry, https://www.xometry.com/resources/3d-printing/5-easy-ways-to-reduce-3d-printing-costs/ (wording not verified) | Partly: Markforged names wall length, not height, as the classic driver. | | Support | Self-supporting to 45°; bridges 5 mm (Hubs) or 10 mm (UltiMaker); support adds more time than any other factor | Protolabs Network, https://www.hubs.com/knowledge-base/supports-3d-printing-technology-overview/ | Markforged, https://www.markforged.com/blog/design-for-3d-printing-part-3-decreasing-print-time ; UltiMaker, https://ultimaker.com/learn/design-for-fff-3d-printing-maximize-your-success/ | Bridge 5 vs 10 mm; overhang 45° vs 45–60° (Prusa). | | Pockets and holes | Holes may save material but not time: a ring took 10 % longer than the full disc; solid can build faster than hollowed | Markforged, same article | Stratasys Direct, same PDF (p. 7) | Yes, by process: Protolabs gives −36 % volume for hollowing, for SLS/MJF/metal, not FDM. | | Bed contact | Largest face on the bed; more contact, less warping | Markforged, https://markforged.com/learn/composites-3d-printing-design-tips | UltiMaker, as above | None; Hubs: strength in XY is 4–5 × that in Z, so load can outrank it. | | Splitting | Fewer supports, fits the bed, shorter build; costs a seam, pins and fit allowance | Prusa, as above | Markforged, Stratasys Direct, as above | Clearance figures differ and are printer-specific. | | Large flat faces | Prone to warping; break up with ribs, round corners | Protolabs Network, https://www.hubs.com/knowledge-base/key-design-considerations-3d-printing/ | Stratasys Direct, same PDF (p. 4) | Severity depends on the machine. Pillowing of large tops: one source only (Polymaker), not in the rule. | | The slicer's part | Infill 10–20 % usual, above 40 % rarely sensible; layer height is the main factor of print time (0.1 → 0.2 mm halves it) | Prusa, https://blog.prusa3d.com/everything-you-need-to-know-about-infills_43579/ | Protolabs Network (infill article above); Markforged, as above | Hubs contradicts itself on infill vs shells for strength; Prusa sides with shells. | ## Test expectations changed - `test/printCheckNote.test.ts`: `ready to print, nothing found` → the new clean text and `Not checked: …`. - `test/standardTool.test.ts`: closing sentence of a standard topic. - `test/blockTool.test.ts`: the keyhole sends a trial batch (`solid.cylinder inspect.interference … sketch.inspect`) before its cuts; `MAGNET` has `around` and `under`; the R54 cases run on a plate that answers probes and holes as the engine does. - `test/designRules.test.ts`: rule list and defaults carry `economy`; the report line names material and print time; ribs. - `e2e/app.spec.ts`: the list of rule switches carries `economy`. - `e2e/solidcraft.contract.spec.ts` (wall plate): the answers must carry `PartBuddy measured that plastic before building, from 0.5 mm under the face on: it is there.` after the keyholes and the magnets and `PartBuddy measured that plastic, from 0.5 mm under the face on: it is there.` after the wood screws. - New: `test/checkOfChange.test.ts` (both routes, no change, bounded wait, two stages, moved revision without change, a failing call that changed the design, undo, two calls at once, unreadable history, drawing tools, told once), `test/bodyMeasures.test.ts`. ## Checked `pnpm run typecheck`, `eslint .` and `vitest run` in `apps/desktop`. Audit harness against SolidCraft 0.40.1: `bloecke` (40 cases), the block cases on flat, sloping and curved faces, the probe shares and the timings above. Not run locally: the app, Playwright E2E and contract tests (CI runs them), a real chat. ## Contract run 43261598187: eight red tests (fixed in e0f821e, tests only) - **Six tests** (standard screw, sketched blocks, snap fit, SVG logo, colour logo, built text) failed with `the assistant is working on the design`. Cause: the tool's answer now waits for the print check of its change (`withCheckOfChange`), so the part shows before the assistant's turn ends, and the tests called the user's action as soon as the mesh showed. Log of the screw test: session started 16:50:13.770, `design:apply-action failed` 16:50:14.710. The tests now wait for the Stop button to go (`turnEnded`) after each first build; no fixed waits. - **Wall plate and clipped cover**: nothing was refused. The page snapshot of both shows the full answer with every expected sentence and `it is there.`; the print check behind each block ends in a list of measures, which the chat renders as a list, so the answer is several elements and a `getByText` across two blocks no longer matches. The tests read the whole answer (`assistant-text`) instead. - **The lock**: bounded and always released. `withCheckOfChange` returns after at most `CHECK_WAIT_MS` (3 s) per changing call, also when the check never comes or the design changed (`test/checkOfChange.test.ts`: bounded wait, check of another design). Measured locally on SolidCraft 0.40.1 with the fake assistant: the turn ended 150 to 350 ms after the part showed in seven builds, 1.7 s in the first one after a cold start. Before this PR the answer did not wait at all. No product change. - All eight tests passed locally one by one against SolidCraft 0.40.1. Not run: the other 49 contract tests, `contract.yml` in CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: ManuelStaggl <17688572+ManuelStaggl@users.noreply.github.com> Co-authored-by: Claude Fable 5.2 <noreply@anthropic.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.
Fixes the severity-A findings of the full audit (2026-11-12) on the ready-to-print check, one commit per point. Values were measured with the audit harness (
tools/audit, casesdruckpruefung-befundeanddruckpruefung-gegenprobe,mechanik-others,normteile-policy-raw) on SolidCraft 0.40.1, profile PLA, 256 mm plate, 0.4 mm nozzle, 0.2 mm layer, before and after.Findings
block, size 300 × 10 × 5ready(370 × 10 × 5 stillblock)blockreadyblock, size 320 × 20 × 20readyblock, size 500 × 200 × 3readycontactcontact0 mm², body "Kugel"readywarn,contact1 mm², body "Kegel"block, "thinner than the nozzle"warn, floor 0.3 under 0.8 mmblockfinding over 6481 mm²block4635 mm², floorwarn1729 mm²warn,thinWall0.8readyready(one withcontactonly)block,thinWall0.2 mm, 364 mm² (exact: 365.2)warn,thinWall0.9 under 0.9thinWall(0.89 still found)readywarn,overhang1600 / 800 / 10 000 mm²warn(contact0 /separate),readyblock,openreadyreadyControls unchanged: cantilever 40 mm
warn, bridge 3 mmready, uniform 0.2 mm wallblock; the five good bodies of the counter-check (cube, box, bracket with 45° chamfer, cylinder, plate with hole) stayready; no mechanism or hinge body reads as open.What changed
sizefinding names its body.PrintCheckProfilecarries the layer height. What lies within 45° of flat (as the body lies) is a floor: warned underminimumFloor= max(0.8 mm, 4 layers), blocked under one layer. Walls keep two lines / nozzle. Rays are spread a nozzle width apart over each triangle and set at its corners; the block counts the area under the limit; a wall of exactly the minimum is not counted.thinWallfindings carryfloor, walls and floors are separate findings. The design rule text takesminimumFloorfrom the same place.BRIDGE_SPAN= 10 mm apart is a bridge and stays out; the design rule text uses the same constant.open(block) and left out of the other checks.Each point has a unit test in
packages/geometry/srcholding the measured error and a counter-case; the wording for the assistant and the chip texts (de, en) follow.Open
structure.jointsnames only the bodies a joint moves, and the audit itself leaves open whether the export keeps jointed bodies together.tools/auditon the audit branch needslayerin its profiles (harness.ts,mechanik-helpers.ts) andbody/floorin the hand-written findings of thepassung-textecases.Checked
pnpm --filter @partbuddy/geometry test(66 after the review, 72 after the second),pnpm exec vitest runinapps/desktop(716, unchanged after the second review),pnpm run typecheckover the workspace, eslint on both packages. Not run: Playwright E2E (one expected text ine2e/app.spec.tswas adapted by hand), the SolidCraft contract tests, the app itself.After the review
Rebased onto main; three more commits. Each point has a unit test with the example of the review.
takes no bridge across a slot or a hole in the ceiling…readyoverhang240 mm² (the shelf; without the lip 300 mm² of a 10 mm shelf)hangs nothing from material that itself ends in the airdoes not take a triangle drawn to a line for a hole in the shellopenEdgesundefinedreadysize,blockchecks a body whose model carries no count of open edges…layerundefinedreadywarn, floor under 0.8 mmmeasures what lies flat in layers…tells a thin floor from a thin wall…LYING_HOLE= 8 mm: Ø 8 not reported, Ø 9 reportedlets a hole lying across the layers print round up to 8 mm…bodies: 50, all 50 named; note says "and 46 more bodies"gathers the bodies with little on the bed into one finding…,says how much it leaves out…Still right: groove ceilings 101.9 / 27.9 / 13.9 mm reported (3057 / 837 / 417 mm² on closed test bodies; harness 3057.96 / 837.96 / 417.96), bridges of 5.97 and 3 mm free, arm of 40 mm reported. The harness cases
druckpruefung-befundeand-gegenprobegive the same statuses and areas as before the review fixes; only the new fields (bodies,layers) are added.Open bodies on SolidCraft 0.40.1 (point 3): sphere Ø 20 (25 504 triangles), cone with a tip (5 242) and a cube with a modelled M5 thread (8 918) each have 0 degenerate triangles and 0 open edges; none reads as open.
How:
shortcut:in the code: a hook whose own back looks up still counts as standing.LYING_HOLE= 8 mm (MakerVerse, the one source of the audit's scale with a size); the rule text takes the constant.thinWallfloors carrylayers,contactis one finding withbodies, the report carriesomitted; the note says "and N more" for bodies and findings.gridBody, andprismBodywith ear clipping instead of a fan).After the second review
Two more commits; what counts as a small overhang (
overhang.ts) is reworked. The reviewer's scripts (cases.mjs,more.mjs,perf.mjs,slot.mjs) were run against the result and their cases taken over as unit tests (bridges.test.ts,overhangSpeed.test.ts).gives the same answer however the part is turned on the bedtakes no bridge across a slot or a hole in the ceiling…overhangSpeed.test.ts(limit 2 s at 100 000)ready(all three)overhang240 mm² (all three)hangs nothing from material that itself ends in the airreads a hole the same however coarsely it is meshedoverhangundefined or NaNreadychecks overhangs at 45° where the profile names no anglebodiessays how much it leaves out…Each bridge, hole, T-slot, counterbore, shelf and box case of the tests is also run turned by 1°, 7.5°, 15°, 30°, 45°, 60°, 75° and 90°, with the same area within 1 %.
How it works now:
shortcut:about hooks is gone.Changed on purpose against the list of cases to hold, because of point 4:
Both still read
warn. Holding: shelf with drip edge 240 mm², groove ceilings 101.9 / 27.9 / 13.9 mm reported (3057 / 837 / 417), 5.97 mm free, bridge of exactly 10 free and 10.001 reported (300), arm 40 mm reported (1200), hole Ø 8 free / Ø 9 reported, the harness casesdruckpruefung-befundeand-gegenprobeunchanged (the five good bodiesready), and sphere, cone and M5 thread from SolidCraft 0.40.1 as before.Known limits:
perf.mjsis a strip floating 5 mm over the bed with no core; nothing of it stands, so it is reported (4712 mm²) where the code before called it small. On a core that reaches the bed the same thread is small (test, 149 760 triangles in about 1.5 s).Fourth round: the cases as tests, the check unchanged
Two commits, both tests only (
ee59e9f,1546201).overhang.tsis as after the second review. Not ready to merge: the false "ready" of the third review is still there.overhangCases.test.ts: 178 cases from the three reviews, each as drawn, turned by 1°, 7.5°, 15°, 30°, 45°, 60°, 75° and 90°, and mirrored (as drawn and at 30°). On the check as committed 151 hold and 27 do not; those 27 run as expected failures (OPENin the file), so the suite is green and a case that comes right says so.overhangSpeed.test.ts: no limit in seconds any more. Each mesh is timed against one a quarter of its size, the fastest of three runs each; four times the triangles may take 12 times as long (one pass: 4, measured 4.2 to 7.3 under load; searching anew from every rim: 16).Why the check was not changed in this round. Two reworks of "what stands" were measured against the 178 cases, neither is committed:
The cause is one: whether an edge stands has to be known along its whole length and across each face, and each facet of a rounding stands on the one below it. On the faces of the mesh that is either a chain without a bound or the surface sampled anew every millimetre, which is slicing by another road; and a body that rests on a reported overhang (a tube lying on the bed) needs the answer from the bottom up, which a surface gives in no order. The proposal is to cut the body into layers and measure per layer what rests on the one below and how far it spans (
clipper2-tsis in the workspace). To be decided before any more work onoverhang.ts.Checked:
pnpm --filter @partbuddy/geometrylint, typecheck and test (251: 224 pass, 27 expected failures, 11 s). Not run: the desktop tests, Playwright, the SolidCraft contract tests. The expected values of the cases were taken over as written in the interrupted round, not measured again against the reviewers' scripts.🤖 Generated with Claude Code