Repository navigation
fix(desktop): standard parts are told by SolidCraft's library and cannot be forged by a description - #356
Merged
Conversation
… part, not 12 named families
Audit S-01, G2, N-normteile-policy-1. The mirror told parts.insert components by a fixed list of
12 family names; SolidCraft 0.40.1 has 13. The countersunk screws partbuddy_mechanism puts in went
to print check, plate and export as printed bodies while the tool said they were standard parts.
The mirror now reads parts.library with the components' metadata and takes a description for a
part when it is a family's name, one of its sizes and a length. A family SolidCraft adds is known
without a change here; a text that only begins like a family ("Hex nut holder") is no part.
Measured against SolidCraft 0.40.1 (tools/audit, normteile-policy): families recognised 12/13 ->
13/13 (185 of 185 over every size); screw joint with head countersunk: structure.standardParts
[] -> [Body1, Body2], STL of the exported bodies 6084 -> 1364 triangles (block and plate alone:
1364).
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ard part
Audit H-01. PartBuddy tells the parts SolidCraft's library put in by their component's description,
and the assistant could write that field: component.metadata.set {description: "Hex nut M3"} on a
lid 0.3 mm thick took the lid out of print check, plate and export.
The policy now refuses component.metadata.set with a description, in both forms SolidCraft 0.40.1
takes (description, values.description) and as a batch step. Other properties, component.rename
and free properties stay allowed; measured, none of them makes a body a standard part.
Measured (tools/audit, normteile-policy-metadata): decide() allow -> deny; after the attempt
structure.standardParts [Deckel] -> [], printed bodies [Box] -> [Box, Deckel], print check ready
-> block ("walls down to 0.3 mm").
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…t of printing Audit S-01 (same recognition for every caller). The to-buy list of the sharing package took every row of assembly.bom with a part number, whatever print check, plate and export made of its bodies. A printed component with a part number was listed to print and to buy. The list now keeps the rows whose components hold standard parts only, by the same structure.standardParts the export leaves out. Measured against SolidCraft 0.40.1: screw joint with two countersunk screws and a printed lid given part_number "ISO 4032 M3": rows listed 2 (2 x ISO 10642 M3x10, 1 x ISO 4032 M3) -> 1 (2 x ISO 10642 M3x10); the lid stays in print check (block) and export. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…SolidCraft runs it Review of the audit fix for H-01. The policy compared command ids exactly, SolidCraft reads them in any case: execute Component.Metadata.Set, Document.Metadata.Set with PartBuddy's own key, Edit.Checkpoint, Doc.recovery_list, Diagnostics.Crash_Reports and Settings.* were allowed and ran. A batch step named by id instead of command, and a batch reading its steps from a script file (path), passed every rule unseen. 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. Measured with SolidCraft 0.40.1 (headless): of 50 spellings of tool, command, argument and parameter names it runs a command id in any case, a step's id, and batch path; tool names, argument names and parameter keys only as written. decide() for those forms: allow -> deny; the description of the printed lid stays unset. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…hat very body for its part Review of the audit fix for S-01 and H-01. A component described as a library part made every body in it a standard part, so a printed lid moved, copied or built into a nut's component, or joined onto the nut, left print check, plate and export. A second channel, body names kept in the design's metadata, took a lid renamed to a remembered name. The list of parts to buy followed assembly.bom, whose rows the assistant can merge or empty through part number and BOM type. A failed parts.library switched the recognition off without a word. One recognition now, behind print check, plate, size list, export and the list to buy: - a library component counts only with the one body the library builds (all 13 families build one); - that body must have the shape SolidCraft builds for the description: solidcraft-cli builds the part alone and both are measured with inspect.measure (volume, area, faces, edges, vertices); - the names in the design's metadata are neither read nor written any more; - the parts to buy are these parts, counted by their placements, with SolidCraft's part number; - parts.library is read once per engine; whatever fails is logged and counts as printed. Measured with SolidCraft 0.40.1 (tools/audit): lid 0.3 mm and an M3 nut, print check before the steps "block". After move_bodies, copy_bodies, activate + box, combine join, box join, the nut removed and the lid moved in, a forged description, a rename into the remembered name: "ready" without the lid -> "block" with the lid in print check and export, in all 10 cases. Parts to buy with the lid given the nut's part number, the nut set to reference, the nut's part number emptied: nut missing or lid listed -> 1 nut, 2 washers in all cases. All 13 families and 185 of 185 sizes stay standard parts, a third of them turned; a part turned, hidden or resized differs from its reference by at most 6e-11. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
ManuelStaggl
force-pushed
the
fix/standard-parts-audit
branch
from
November 12, 2026 14:16
93206cc to
9f88b5d
Compare
…ept, and told by every face Second review of the audit fix for S-01 and H-01. The mirror waited for solidcraft-cli to build each new part (0.4 s for one, 4 s for 30, 43 s for all sizes, with every start of the app), a failed run was tried again with every mirror, and volume, area and three counts did not fix the shape: a ring of other radii and height met the M3 washer on all five to 2.6e-11. - The mirror no longer waits. A part without a reference counts as printed until it is built in the background; then the design is mirrored once more. Export, slicer, size list and the parts to buy wait for the references, so nothing leaves with a screw in it. - References are kept in the cache folder under the program's version, size and date. A part SolidCraft does not build is remembered with its reason; a run that gave no answer is not tried again for five minutes. A run ends after 20 s plus 2 s a part instead of 2 min. - A part asked for while it is being built is not built a second time. - A shape is also the area, outline and radius of each face, from inspect.measure of the face. model.faces and model.edges give mesh values, up to 2 % apart between a part straight and turned. - Every body left printed is logged with its name and the reason. Measured with SolidCraft 0.40.1 (tools/audit): the mirror's question for references 4 to 14 ms (before: 0.37 s to 43 s); background build 0.7 s for one part, 11 s for 30, 98 to 112 s for all 185 sizes, once per SolidCraft; after a restart 0.1 s from the file. A refused part (M3x5000): one run of 0.2 s, then none (before: one with every mirror). The ring is printed; 13 of 13 families and 185 of 185 sizes stay standard parts, a third of them turned. Two designs saved by the audit and opened again: 13 of 13 and 2 of 2 parts told. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…icture reads only a photo
Third review of the audit fix for H-01. The policy read the arguments of execute and batch only.
screenshot {path} was let through and SolidCraft wrote its PNG over any file in the folders it
may use: measured, 442 140 bytes over a JSON file in the cache folder; the project and the design
lie in those folders too. compare_picture {reference} read any PNG or JPEG there.
Every tool of SolidCraft the policy lets through is now refused a file argument (path, file,
reference, output). Two are kept: compare_picture with the photo PartBuddy saved for the user, as
the photo note tells the assistant to use it, and skill_reference with a document's plain name.
Checked against tools/list of SolidCraft 0.40.1: of the tools let through, screenshot (path),
compare_picture (reference), skill_reference (file) and batch (path, refused before) take a file;
the others answer "unknown argument". skill_reference refuses a path by itself.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…oes out unchecked Third review of the audit fix for S-01 and H-01. - Only SolidCraft's own answer that it does not build a part (the parts.insert step failing with its reason, or another part built for the words) is kept in the file. A crash, an answer that does not read or a script that cannot be written says nothing about the part: it is remembered until PartBuddy ends, with a line in the log. One crashed "Hex nut M3" no longer makes every M3 nut a printed part over restarts. - After a run without an answer a timer builds the parts once the five minutes are over and the design is mirrored. Every background build ends in one mirror, whatever it brought. - Export, slicer and the list of parts to buy try a failed reference once more; when a body still cannot be checked they write nothing and say why, instead of exporting the screw. - A body whose part is still being built is listed in structure.pendingParts and left out of the print check until the mirror that follows says whether it is a standard part or a printed one. - The kept file carries its format; an entry that is no complete reference is dropped and built again. - A reference built while a mirror fails is not lost: that mirror is tried once more. - With more than 200 descriptions the export waits for every run, not for the first. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…only arguments it is known for Last review of the audit fix for H-01, points 4 and 5. The assistant may hand SolidCraft one kind of file: a photo the user sent. The policy took any path of that form for one: a photo folder of another project, a network share (which alone makes SolidCraft reach for the server), a device path, a relative path. It now knows the files PartBuddy itself kept and named to the assistant in this run and compares the resolved path with them; a path that starts with two slashes or is not absolute is refused before that. The file arguments of SolidCraft's tools were refused by four names on the top level. Each tool let through now 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, as the tool does. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…ils; no export locks Last review of the audit fix for S-01, points 1, 2, 3 and 6. - A body left out of the print check while its part is built stayed out for good when the mirror that follows failed. The last revision now goes out again with the body as a printed one, and the owed mirror is tried three times, five seconds apart. - Without solidcraft-cli no part can ever be checked, and export, slicer and sharing package wrote nothing. Such bodies now count as printed, go along, and the user reads their names with the result; nothing is listed to buy. - A parts.library that fails or is rejected leaves nothing unchecked: no body can be a standard part then, and no export is held up by it. - A part whose run gives no answer gets three runs, by one timer for all that wait; the timer is called off with the design or the engine. The mirror's own retries end after three. 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 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, casesnormteile-policy-*) against SolidCraft 0.40.1, headless.How a standard part is told now
One recognition (
boughtPartsinmirror.ts) stands behind print check, plate, size list, export and the list of parts to buy. A body is a standard part only whenparts.insertwrites: a family's name, one of its sizes and a length, all read fromparts.library(no list of names in PartBuddy);solidcraft-cli runbuilds the part alone, and both are measured withinspect.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
countersunk_socket_screwmissing)partbuddy_mechanism {kind: screw, head: countersunk}:structure.standardParts[][Body1, Body2]head: capThe tool's sentence "the screws are in the design as standard parts" is true for both heads.
mechanismTool.tsandmechanisms.tsare 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 refusescomponent.metadata.setwith 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.decide()structure.standardPartsafter the attempt[Deckel][]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 asid, and a batch's steps from a script file (path); tool names, argument names and parameter keys only as written (Execute,Params,Path,Keyare 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'sidis decided like itscommand;batchwith apathis refused.decide()forComponent.Metadata.Set,Document.Metadata.Setwith PartBuddy's key,Edit.Checkpoint,Doc.recovery_list,Diagnostics.Crash_Reports, a step{id: ...}andbatch {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:component.move_bodiesof the lid into the nutcomponent.copy_bodiesinto the nutcomponent.activatethe nut +solid.boxsolid.combinejoin of a plate onto the nutsolid.box {operation: join}onto the nutsolid.removethe nut, lid moved inStage 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 toSHCS 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 givenpart_number: "ISO 4032 M3"(BOM: one row, components [2, 5], count 2), the nut set tobom_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.libraryswitched 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.
part-references.jsonin the cache folder (kept under the program's version, size and date)Socket head cap screw M3x5000)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.facesandmodel.edgeswere measured and are not used: they give values of the display mesh, up to 2 % apart between the same part straight and turned;inspect.measureof 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.
cliReferenceswas 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 bysolidcraft-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
parts.insertstep 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".supervisor.test.ts"exports no standard part, waits for its reference, and says so instead of exporting a body it could not check".structure.pendingPartsandusePrintCheck.tsleaves 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 fix(desktop): the assistant sees the print check of its changes, blocks measure before they cut #357 (itsmeasuringfield). Test:mirrorRevision.test.ts, "does not wait for a part SolidCraft is still building…".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".readygoes on until nothing is being built (at most 20 runs). Test: "waits for every part when there are more than one run takes".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 arecompare_picturewith the photo PartBuddy saved for the user (the photo note tells the assistant to use it) andskill_referencewith a document's plain name. Checked againsttools/listof 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) andbatch(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
solidcraft-climissing or not answering); it does not offer to export anyway.shortcut:inmirror.ts).screenshot {path},compare_picture {reference},document.metadata.set {properties}) and S-30 (C) are not part of this change.Not checked
e2e/solidcraft.contract.spec.ts) and the app itself were not started. Two of them depend on the reference fromsolidcraft-cliinside the app ("standard part" on a seatedSHCS 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.body "<name>" is described as "<description>" but is not that part (…)or…, which SolidCraft did not build to hold it against (<reason>).parts.insert.Last review (commits
df8c844,ebef1d6)Six points on
46e330aandc38f5b6. Where this section and "Still open" above differ, this section holds.pendingPartsstayed 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 withoutpendingParts, 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".solidcraft-cliexport, 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".parts.librarylocked 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".//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".tools/listof SolidCraft 0.40.1 gives for it (withoutscreenshot's andbatch'spath); any other argument is refused, also as a batch step. For engine commandsfile(sketch.replace) is refused likepath, andinspect.compare_picturereads 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".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:
parts.librarya 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).Checked in this round: eslint on the changed files,
typecheck, and vitest formirrorRevision,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