Skip to content

fix(desktop): the assistant sees the print check of its changes, blocks measure before they cut - #357

Merged
ManuelStaggl merged 30 commits into
mainfrom
fix/assistant-check-rules
Nov 12, 2026
Merged

ManuelStaggl merged 30 commits into
mainfrom
fix/assistant-check-rules

Conversation

@ManuelStaggl

@ManuelStaggl ManuelStaggl commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

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

ManuelStaggl and others added 13 commits November 12, 2026 13:45
…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>
…ll that made it

Audit R1: reading the check after the last change was only asked of the
assistant, so a turn could end on a blocked part it never saw checked. The
work log now hands every answer of a call that moved the design's revision
to the check: it waits up to 5 s for the mirror and the check of that
revision and puts the findings behind the answer, or says that the check is
still running. All three routes share this tool chain.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… did not

Audit R3: "ready to print, nothing found" and "the print check still judges
what you build" promised more than the check covers. The note now lists what
was checked and what was not (strength and layer direction, chosen fits and
hole allowances, fine details, bed-edge chamfers), and the standard topics
say that the check does not test their rules.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…s cuts need

Audit R54: the blocks named what the surrounding plastic must be only in
their answer and built without measuring. A keyhole and a magnet pocket are
now measured on trial before anything is cut (the pocket with what must stay
round and under it, laid in as a probe body), the seat of an insert is
checked by the volume it takes, and a block that leaves the body in pieces
is taken back, each with the measured value. What is still not measured
(wood screw, the wall round an insert) says so in the answer.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…sees what a change saved

Reported 2026-11-12: the user had to ask for print time and material to be
optimised every time. A fifth design rule group (on by default, switched off
in the settings or with partbuddy_design_rules) tells the assistant what the
shape decides of both, with the wall and layer sizes of the printer in use,
and what stays the slicer's. The print check now carries per body the solid
volume, the height and layers as it prints, the area on the bed and the area
that leans past the overhang limit, so the numbers come with every check.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@ManuelStaggl ManuelStaggl changed the title fix(desktop): the assistant sees the print check of its own changes, and blocks measure before they cut fix(desktop): the assistant sees the print check of its changes, blocks measure before they cut Nov 12, 2026
ManuelStaggl and others added 8 commits November 12, 2026 15:16
…ules

# Conflicts:
#	apps/desktop/src/main/agents/blockTool.ts
#	apps/desktop/test/blockTool.test.ts
…when the design changed

Review of PR 357. With two bodies or a joint the mesh check now goes ahead
of the clearance and joint measures and is told as preliminary, so no step
waits for SolidCraft's measuring (waits set from timings at the engine). A
call counts as a change by the undo history, not by the revision that trials
and saves move too. The same verdict is told in full once per turn and then
in one line. The drawing tools are changing tools. A body that cannot be
measured no longer holds the report back, and the measures say what solid
volume overstates and carry a shell estimate.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Review of PR 357: half to three quarters of the wall is the rule of injection
moulding against sink marks; on a 0.9 mm wall it gave a rib of one line,
under the thinnest wall the print check accepts.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… curved faces pass

Review of PR 357. The plastic a block needs is measured from under the face
on, so a curved or sloping face is not read as a gap. The seat of an insert
and a countersunk hole are given no direction: it is read from what the cut
took out of the body, and the wall round and the plastic under them are
measured after the cut, which is taken back when they are missing (a plate
just as thick as the seat is deep, a pilot hole through the part). The
threshold stands on shares measured at SolidCraft 0.40.1.

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>
… hole along its own way

Second review of PR 357. SolidCraft caps the depth of its undo history at
200, so a block is undone step by step until bodies, volumes and feature
count are as before, never further than it sent, and says so when that
fails. Each hole of a block is cut and measured by itself, so holes that
take unequal amounts no longer share one skewed axis. The plastic is
measured from 0.5 mm under the face on, a seat must also cut its whole
cylinder (its rim at a chamfer does not), the wood screw's stock is the
screw's diameter round its hole, and the answers say what was measured.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…s on a preliminary note

Second review of PR 357. At the cap of SolidCraft's undo history a moved
revision counts as a change, or no step past it got its check. A preliminary
check says how to get the full one in its short form too, and from
partbuddy_view that the rest is coming. A clearance or joint measure that
failed is named as not measured. Whether a check is unchanged is read off
all its findings, not the lines shown.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
ManuelStaggl and others added 2 commits November 12, 2026 17:08
…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>
Base automatically changed from fix/print-check-audit to main November 12, 2026 16:22
ManuelStaggl added a commit that referenced this pull request Nov 12, 2026
…not be forged by a description (#356)

Fixes the severity-A findings of the full audit (2026-11-12) on telling
bought standard parts from printed bodies, and the gaps an independent
review found in the first version of this change. Everything is measured
with the audit harness (`tools/audit`, cases `normteile-policy-*`)
against SolidCraft 0.40.1, headless.

## How a standard part is told now

One recognition (`boughtParts` in `mirror.ts`) stands behind print
check, plate, size list, export and the list of parts to buy. A body is
a standard part only when

1. its component's description is one `parts.insert` writes: a family's
name, one of its sizes and a length, all read from `parts.library` (no
list of names in PartBuddy);
2. the component holds that one body and nothing else (all 13 families
build exactly one);
3. the body has the shape SolidCraft builds for that description:
`solidcraft-cli run` builds the part alone, and both are measured with
`inspect.measure`: volume and area, the counts of faces, edges and
vertices, and of each face its area, the length of its outline and its
radius (all within 1e-6).

Whatever cannot be read, measured or built counts as printed and is
logged with the body and the reason: a screw in the print check shows, a
printed part missing from it does not. The mirror does not wait for a
part being built; export, slicer, size list and the parts to buy do.

## S-01 / G2 / N-normteile-policy-1: one of 13 library families was
printed and exported

| | before | after |
|---|---|---|
| families recognised (one part each) | 12 / 13
(`countersunk_socket_screw` missing) | 13 / 13 |
| every size of every family, plus odd lengths, a third turned to [1, 2,
3] | not measured | 185 / 185 |
| `partbuddy_mechanism {kind: screw, head: countersunk}`:
`structure.standardParts` | `[]` | `[Body1, Body2]` |
| bodies exported | Block, Plate, Body1, Body2 | Block, Plate |
| STL of the exported bodies | 6084 triangles | 1364 (Block and Plate
alone: 1364) |
| control `head: cap` | 2 standard parts, 1456 = 1456 | unchanged |

The tool's sentence "the screws are in the design as standard parts" is
true for both heads. `mechanismTool.ts` and `mechanisms.ts` are
untouched.

## H-01: the assistant could turn a printed body into a "standard part"

`execute component.metadata.set {component, description: "Hex nut M3"}`
took a 0.3 mm lid out of print check, plate and export. The policy
refuses `component.metadata.set` with a description (`description`,
`values.description`, as a batch step). Since review point 2 the forged
description would no longer work either: the lid is not shaped like the
nut.

| | before | after |
|---|---|---|
| `decide()` | allow | deny |
| `structure.standardParts` after the attempt | `[Deckel]` | `[]` |
| print check | ready, "nothing found" | block, "walls down to 0.3 mm" |

## Review

**1. The policy compared command ids exactly; SolidCraft runs them in
any case.** Measured over 50 spellings of tool, command, argument and
parameter names: SolidCraft takes a command id in any case
(`Component.Metadata.Set`, `File.Export`), a batch step's command as
`id`, and a batch's steps from a script file (`path`); tool names,
argument names and parameter keys only as written (`Execute`, `Params`,
`Path`, `Key` are unknown or ignored). Every rule now sees the
lower-case, trimmed id, normalised in one place for execute, batch steps
and nested steps; a step's `id` is decided like its `command`; `batch`
with a `path` is refused. `decide()` for `Component.Metadata.Set`,
`Document.Metadata.Set` with PartBuddy's key, `Edit.Checkpoint`,
`Doc.recovery_list`, `Diagnostics.Crash_Reports`, a step `{id: ...}` and
`batch {path}`: allow -> deny. Test: `agent.test.ts`, "decides a command
id in any spelling SolidCraft runs".

**2. Bodies in a library component counted unchecked.** A box, a 0.3 mm
lid (print check `block`) and an M3 nut; the steps sent past the policy:

| steps | before | after |
|---|---|---|
| `component.move_bodies` of the lid into the nut | ready, lid a
standard part | block, lid printed (2 bodies: stage 1) |
| `component.copy_bodies` into the nut | reproduced by the review |
block (stage 1) |
| `component.activate` the nut + `solid.box` | reproduced by the review
| block (stage 1) |
| `solid.combine` join of a plate onto the nut | reproduced by the
review | block, nut body printed: 310.17 mm³ and 24 faces against 45.91
mm³ and 9 (stage 2) |
| `solid.box {operation: join}` onto the nut | reproduced by the review
| block (stage 2) |
| `solid.remove` the nut, lid moved in | not measured | block (stage 2)
|
| description "Hex nut M3" on the lid's component | ready (H-01) | block
(stage 2) |
| nut described as "Hex nut M4" | not measured | nut printed (stage 2) |
| "Plain washer M3x8" on the lid, beside the nut | not measured | lid
printed, nut still a standard part |

Stage 2 comes from a reference, not from library dimensions: a world
bounding box changes with the part's direction, volume and area do not
(turned, hidden, resized: at most 6e-11 off the reference). What the
reference costs and where it is kept: see the second review, point 1.
Tests: `structure.test.ts` ("counts every body of a library component as
printed…", "takes a body for the part only when it has the shape…"),
`partReference.test.ts`, `mirrorRevision.test.ts`.

**3. Body names kept in the design's metadata.** The key is neither read
nor written any more; parts put in from the menu are library components
like any other. Measured: key set to `["SHCS M3x12"]`, nut renamed, lid
renamed to `SHCS M3x12`: the lid stays printed, the renamed nut stays a
standard part. The policy still refuses writes to the key by its prefix.

**4. Parts to buy.** No longer `assembly.bom`: the list is the standard
parts themselves, counted by their placements through subassemblies,
with the part number SolidCraft gives the reference. A nut, a washer in
a subassembly placed twice, a printed lid: with the lid given
`part_number: "ISO 4032 M3"` (BOM: one row, components [2, 5], count 2),
the nut set to `bom_type: reference` (BOM: no nut), the nut's part
number emptied: the list is 1 x Hex nut M3, 2 x Plain washer M3 in every
case. Test: `structure.test.ts`, "lists the parts to buy from the
standard parts themselves".

**5. A failed `parts.library` switched the recognition off silently.** A
failed batch, a rejected request, no results and an empty library each
write a line to the log; the library is read once per engine connection
and again after a failure. Test: `mirrorRevision.test.ts`, "says so in
the log when parts.library fails or is empty".

**6. A fallback to the former twelve names is left out.** A family
SolidCraft renames or a size it drops cannot be built by the installed
SolidCraft, so there is nothing to hold the body against; a fallback by
name alone would be the gap of point 2 again. Such a part counts as
printed and is logged.

## Second review

**1. The reference blocked the mirror, and a failed run was tried again
with every mirror.**

| | before | after |
|---|---|---|
| what the mirror waits for a new part | 0.37 s (1 part), 4.2 s (30), 43
s (all sizes), with every start | 4 to 14 ms: it asks and goes on |
| build of the references | in the mirror | in the background: 0.7 s (1
part), 11 s (30), 98 to 112 s (all 185 sizes), once per SolidCraft
program; then the design is mirrored once more |
| after a restart of the app | built again | 0.1 s, read from
`part-references.json` in the cache folder (kept under the program's
version, size and date) |
| a part SolidCraft refuses (`Socket head cap screw M3x5000`) | one
failed process with every mirror | one run of 0.2 s, then remembered
with its reason, over restarts too |
| a run that gives no answer | up to (1 + N) x 2 min with every mirror |
ends after 20 s + 2 s a part (the slowest single part builds in 0.5 s);
its parts are not tried again for 5 min |

Until a part's reference is there its body counts as printed, so for
that moment the print check sees the screw. What leaves PartBuddy does
not: export, send to slicer, the size list and the parts to buy read the
standard parts anew and wait for their references. The build takes two
runs now (how many faces a part has is known only once it is built),
which is why it is slower than the one the review timed.

**2. The same reference was built twice by the mirror and the list of
parts to buy.** A description being built is not started again; whoever
waits, waits for the run that is under way.

**3. Five numbers did not fix the shape.** A ring of R 4.0251, r 3.3699,
1 mm high has the volume, area and counts of the M3 washer to 2.6e-11.
The shape now also holds each face's area, outline and radius.
`model.faces` and `model.edges` were measured and are not used: they
give values of the display mesh, up to 2 % apart between the same part
straight and turned; `inspect.measure` of a face is exact. Measured: the
ring, described as "Plain washer M3", stays printed; all 185 sizes stay
standard parts, a third of them turned.

**4. `cliReferences` was untested.** `partReference.test.ts`, with the
program's runs injected: a reference is kept and no process runs for it
again; a refused part is remembered, also over a restart; a part that
cannot be built lets the others stand; a run without an answer is tried
again only after a while; the script file is removed; a background build
asks for exactly one more mirror (`mirrorRevision.test.ts`: "mirrors the
delivered revision again, once however often it is asked"); two askers,
one build; the ring against the washer.

**5. Older designs.** No saved design with standard parts lies in the
repository (`apps/desktop/e2e`, `tools/fixtures`, `tools/templates`,
`site`: none). Measured instead on the two designs the audit saved on
2026-11-12 (`families.sc`, one part of each family; `screw-cap.sc`, a
screw joint), opened again by `solidcraft-cli run --in`: 13 of 13 and 2
of 2 parts are told after the rebuild. Which SolidCraft wrote them
(0.40.0-rc.4 or 0.40.1) the files do not say.

## Third review

- **a. A crash was kept as "SolidCraft does not build this part".** Only
SolidCraft's own answer is kept in the file: the `parts.insert` step
failing with its reason, or another part built for the words. A crash
exit code, an answer that is no JSON, a script that cannot be written or
removed are remembered until PartBuddy ends (one try per start, and one
more by an export), with a line in the log. Test:
`partReference.test.ts`, "does not take a crash, or an answer that does
not read, for SolidCraft refusing the part".
- **b. Nobody mirrored again after a run without an answer, and an
export could go out with the screw.** A timer builds the parts once the
five minutes are over; every background build ends in one mirror.
Export, send to slicer, size list and the parts to buy try a failed
reference once more and, when a body still cannot be checked, write
nothing and fail with the reason, which the export's existing error
notice shows ("standard parts could not be checked, so nothing was
written (…). Try again in a moment."). Tests: "tries a program that gave
no answer again after a while by itself…", `supervisor.test.ts` "exports
no standard part, waits for its reference, and says so instead of
exporting a body it could not check".
- **c. The print check saw the screw while its reference was built.**
Such bodies are in `structure.pendingParts` and `usePrintCheck.ts`
leaves them out; after the build they are standard parts or printed
bodies again. Marking the report as provisional for that moment is left
to the merge with #357 (its `measuring` field). Test:
`mirrorRevision.test.ts`, "does not wait for a part SolidCraft is still
building…".
- **d. The kept file was taken on trust.** Its key carries a format
number; an entry is used only as a complete reference (every field
there, every number finite, as many face areas as faces), else dropped
and built again. Test: "builds anew what a kept file holds in another
form, and takes no entry on trust".
- **e. `refresh()` during a failing mirror was lost.** That mirror is
tried once more. Test: "does not lose a reference built while a mirror
fails, and mirrors nothing again after a reset".
- **f. More than 200 descriptions.** `ready` goes on until nothing is
being built (at most 20 runs). Test: "waits for every part when there
are more than one run takes".
- **g. `screenshot {path}` overwrote any file in the allowed folders**
(own commit). Every tool of SolidCraft the policy lets through is
refused a file argument (`path`, `file`, `reference`, `output`); kept
are `compare_picture` with the photo PartBuddy saved for the user (the
photo note tells the assistant to use it) and `skill_reference` with a
document's plain name. Checked against `tools/list` of 0.40.1:
`screenshot` (path, wrote 442 140 bytes over a JSON file),
`compare_picture` (reference, reads a PNG or JPEG), `skill_reference`
(file, refuses paths itself) and `batch` (path, refused since the first
review) take a file; the other tools answer "unknown argument". Test:
`agent.test.ts`, "refuses a file argument on every tool of SolidCraft it
lets through, but for a photo the user sent".

## Still open

- While a part's reference is built (0.7 s once per part and SolidCraft
program; 0.1 s after each start of the app) its body is left out of the
print check, and the report does not yet say it is provisional (see c).
- An export fails with a notice when a standard part cannot be checked
(SolidCraft's `solidcraft-cli` missing or not answering); it does not
offer to export anyway.
- A shape is as strong as these measures: a body that meets volume,
area, counts and every face's area, outline and radius of a library part
within 1e-6 would pass. For the washer these fix the ring; no other body
was tried.
- A library component with two bodies counts as printed altogether, the
library's body too (marked `shortcut:` in `mirror.ts`).
- A real part whose description is changed in SolidCraft itself counts
as printed.
- N-normteile-policy-4 and -5 (B: `screenshot {path}`, `compare_picture
{reference}`, `document.metadata.set {properties}`) and S-30 (C) are not
part of this change.

## Not checked

- The contract tests (`e2e/solidcraft.contract.spec.ts`) and the app
itself were not started. Two of them depend on the reference from
`solidcraft-cli` inside the app ("standard part" on a seated `SHCS
M3x12`, two standard parts after a screw joint); the same flows pass in
the harness. One expectation changed: the screw joint test waits up to
30 s for the two "standard part" labels (they follow a mirror after the
bodies show), as the other test already does.
- Designs written by SolidCraft 0.30.1 or a 0.40.0-rc, with parts put in
from the menu: none is at hand and no other SolidCraft is installed
here. If such a part's description or shape differs, its body counts as
printed and the log names it: `body "<name>" is described as
"<description>" but is not that part (…)` or `…, which SolidCraft did
not build to hold it against (<reason>)`.
- SolidCraft's window as the engine (not headless), and a part placed
several times by one `parts.insert`.



## Last review (commits `df8c844`, `ebef1d6`)

Six points on `46e330a` and `c38f5b6`. Where this section and "Still
open" above differ, this section holds.

1. **`pendingParts` stayed for good when the mirror after the build
failed.** The body was left out of the print check until a mirror that
never came. The scheduler keeps the last revision; when a mirror fails
and that revision has bodies waiting, it goes out again without
`pendingParts`, so the body is checked as a printed one. The owed mirror
is tried three times, 5 s apart, and a mirror that works ends the debt.
Test: `mirrorRevision.test.ts`, "hands a body that waited for its part
back to the print check when the mirror after fails, and tries that
mirror a few times only".
2. **Without `solidcraft-cli` export, slicer and sharing package wrote
nothing, for good.** A missing program is its own state (`unavailable`),
not a failed run: such bodies count as printed, go along, and the
result's notice names them ("Included as printed parts, because
PartBuddy could not check here whether they are bought standard parts
…", German and English). Nothing is listed to buy. A run that fails for
now still writes nothing. Tests: `supervisor.test.ts` (the export test,
last block: `{ unverified: ['Nut'] }`, the nut exported, no hardware),
`partReference.test.ts` "says that no part can be built here when
solidcraft-cli is missing, without a run or a timer".
3. **A failing `parts.library` locked the export of a design without any
standard part.** Without the library no body can be taken for a part, so
nothing is left unchecked; a rejected request counts like a failed
batch. Test: `mirrorRevision.test.ts`, "leaves nothing unchecked when
parts.library fails, so an export is not held up by it".
4. **The photo exception was a pattern of the path.** The policy
compares the resolved path with the files PartBuddy kept and named to
the assistant in this run; UNC, device and relative paths are refused
before that. Refused in the test: another project's photo folder, a
photo not kept, `//server/share`, `\\server\share`, `\\.\`, `\\?\`,
relative paths, an 8.3 name, `..`, another file in the folder. Test:
`agent.test.ts`, "takes for the user's photo only the file PartBuddy
kept, not a path that looks like one".
5. **The file lock hung on four argument names.** Each tool let through
has the list of arguments `tools/list` of SolidCraft 0.40.1 gives for it
(without `screenshot`'s and `batch`'s `path`); any other argument is
refused, also as a batch step. For engine commands `file`
(`sketch.replace`) is refused like `path`, and `inspect.compare_picture`
reads only a kept photo. Test: `agent.test.ts`, "lets a tool of
SolidCraft through only with the arguments it is known to take, and with
no file but a photo the user sent".
6. **Timer chains without an end.** A part whose run gives no answer
gets three runs, by one timer for all parts that wait instead of one per
run; the timer is called off with the design or the engine (`forget`).
The mirror's retries of point 1 end after three. An export still gives a
part one more run. Tests: `partReference.test.ts` "gives up a program
that never answers after three runs, and waits for nothing once the
design is gone", and the scheduler test of point 1.

Left open by this round:

- With a failing `parts.library` a real screw in the design is exported
as a printed body with a line in the log only; the user gets no notice
(unlike point 2, where the bodies are known by name).
- After the three retries of point 1 the body stays checked as a printed
one until the next change of the design mirrors it again.

Checked in this round: eslint on the changed files, `typecheck`, and
vitest for `mirrorRevision`, `partReference`, `supervisor`, `agent`,
`photo`, `structure`, `i18nKeys` (all pass). Not run: the whole unit
suite, E2E, the contract tests against a real SolidCraft, the app
itself; the notice of point 2 was not looked at in the window.

🤖 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>
ManuelStaggl and others added 3 commits November 12, 2026 17:34
… 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>
…ules

# Conflicts:
#	apps/desktop/src/main/print/printCheckNote.ts
#	apps/desktop/src/main/print/printProfiles.ts
#	apps/desktop/src/renderer/src/print/report.ts
@ManuelStaggl
ManuelStaggl force-pushed the fix/assistant-check-rules branch from ae1fe37 to 89b9847 Compare November 12, 2026 16:41
ManuelStaggl and others added 3 commits November 12, 2026 18:27
…uilding

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ole answer

Since the tool's answer waits for the print check of its change, the part
shows before the assistant's turn ends; a user action sent right then was
refused ("the assistant is working on the design"). The first-build sends
now wait for the Stop button to go. The wall plate and the clipped cover
read the answer as a whole: the check behind each block ends in a list of
measures, which splits the answer into several elements.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@ManuelStaggl
ManuelStaggl merged commit 32d95ae into main Nov 12, 2026
8 checks passed
@ManuelStaggl
ManuelStaggl deleted the fix/assistant-check-rules branch November 12, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant