A whole circle is one closed edge now, so the outer wall and the hole wall each have a seam; the
rig counts seam edges (4005, 4006), and 1c490ddd's count without them broke those cases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
SMLib is the only kernel now. cargo xtask kernel-cases ran every case once
per kernel asked for (--kernel default|smlib|both), setting
SOLIDCRAFT_KERNEL per run, and wrote a column per kernel; solidcraft-cli no
longer reads that variable.
The rig and the ladder run each case once, through solidcraft-cli built with
--features smlib (cargo run passes it), and report one result per case:
--kernel and the env var are gone. Each run has an output folder (--out,
default target/kernel-cases, target/kernel-ladder for the ladder) holding
results.json (every case's full result, which --results used to write on
request) and dumps/<area>-<case>/: every case runs with
solidcraft-cli kernel-cases --dumps=<out>/dumps, so a failed SMLib call
leaves its inputs for replay.
The ladder's golden holds one entry per case: golden.json is the last
recording's smlib entries, which predate SMLib running every case (its
reason says so); record it again after the first SMLib-only run.
docs/kernel-cases.md (until the next run regenerates it),
tests/kernel-cases/README.md, docs/kernel-smlib-sweeps-shell.md (its truck
column and comparisons dropped), xtask's and solidcraft-cli's usage text:
one kernel. Nothing was run.
clippy -p xtask --all-targets: clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
A rig case whose SMLib call fails left nothing to replay it from: nothing called
solidcraft_smlib::set_dump_dir. `solidcraft-cli kernel-cases --dumps=DIR` now points SMLib's
dump folder at DIR/<area>-<case> before each case runs, so a failing call leaves its
arguments and B-reps there (smlib_brep_read reads them back into a scratch C program), and
turns it off after the run. Off without the flag. The kernel exposes it as
smlib_dump_dir, beside smlib_built (the CLI reaches SMLib only through the kernel).
(not built: base in progress)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
The case expected two bodies for a combine join of an arm 0.01 mm clear of a
ring. A join's tool that meets no target goes into the target as a separate
lump and the tool body is consumed (doc eval Operation::Join; the engine test
join_meeting_nothing_keeps_the_first_body): one body, Ring, of two shells,
with the volumes' sum. The case's point, nothing fuses, is kept: shells 2 and
the summed volume.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
ladder/05/011 expected 8 faces (the stock's 6 and one per hole). With equal
radii the two walls cross along two ellipses that meet at the two points
where the walls are tangent (x = 25 and 35, y = 20, z = 10), so each wall is
left in two pieces that touch only at those points, with no edge between
them: separate faces, 10 in all. SMLib makes those 10 (volume exact); truck's
8 came with an invalid record. Derivation in the case's method.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
The case's own method says one body with an inner shell (the void of
radius 18.5 inside the ball of radius 40), but without a "shells" entry
the rig asks one shell per body. It now asks two, as the method derives.
SMLib still fails the case on its volume: 294558 mm3, near the sum of
the two balls (294604) rather than their difference (241561), so the
inner shell is not taken as a void.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
The kernel runs exact helical sweeps and never resizes; the check that
refuses a thread off its hole's or shaft's size is the engine's sugar
(AGENTS.md). So the seven cases (995, 1443, 2152, 2969, 3366, 2369,
2705) go back to expecting that refusal and its message, as 1375e905 had
them, reversing 1588dd70.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All operations move to SMLib at once, so the selector is no longer the booleans' own:
`SOLIDCRAFT_KERNEL=smlib|truck` (read once, unset is truck, the same refusals for an unknown
value or SMLib in a build without it) is answered by `solidcraft_kernel::backend()`
(crates/kernel/src/backend.rs), which every operation's dispatch calls. The boolean's SMLib path
moves to smlib_boolean.rs. The regression rig's kernel check (`boolean_kernel`) now answers from
the switch, and the rig and CLI set SOLIDCRAFT_KERNEL.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
The 119 surface/surface intersection cases of jyviko/ssi-corpus (b04c8a6,
MIT OR Apache-2.0), gzip-compressed, with its licence texts and a README
on the case format and the scoring rules. The 25 y23 cases come from
Yang, Jia & Yan 2023 (ACM TOG 42(6) Art. 211, CC-BY 4.0); both are in
ATTRIBUTION.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
784, 1753 and the two 1666 lofts came from holes in exported meshes, so
each now also requires a mesh with no open or over-shared edge. On today's
kernel 784 and both lofts are watertight; 1753's bowl, its rim cut by a
cylinder, still has 3 open mesh edges.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two faults made simple curved solids look open (#818's blind hole: 80 open edges, a stadium
slot: 52):
- It meshed with `refine`, the measuring mesh, which splits triangles across curved faces on
their own and leaves T-junctions on the edges they share with neighbours. It now checks the
display mesh, whose faces share each edge's points.
- It matched vertices by rounding to a 1e-6 mm grid, which splits points a rounding error apart
across a cell boundary. It now joins vertices closer than `tolerance.weld_mm` (default 1e-4 mm).
Both cases now have 0 open edges, also with the weld at 1e-6 mm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2822, 3952 and 4149 cut long modelled threads into filleted or revolved
bodies; each takes minutes on today's kernel, so they get timeout_s 600
instead of the default 180.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For the history areas' cases (threads, tessellation):
- A count may be an inclusive range `[min, max]`, where the geometry only bounds it (a modelled
thread's faces).
- `"watertight": true` meshes each body (chord tolerance `tolerance.mesh_mm`, default 0.01 mm)
and requires no open and no over-shared edges, with vertices matched by position.
- `"timeout_s"` gives a case longer than the run's timeout, at most 30 min.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- tests/kernel-cases/sources.json gives each looked-at number's kind: an open or closed issue, a
merged PR, one of the frozen truck-geometry PRs, a kernel-paused PR. The report's coverage by
source counts how many of each were looked at, became cases or were skipped.
- crashes/2357: one join of a ring of 90 overlapping cylinder segments. The right answer is one
closed ring body. The old kernel still panics in its intersection curves and runs past the
timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2969's description named another product's behaviour; 1443's and 2152's
still described the refusal. Each now says the hole is drawn at the
thread's nominal size and gets the fitted thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reverts 1375e905. A modelled thread fits by default: a hole too wide for it
is filled to the tap drill, a shaft too thick is turned down to the major
diameter, so the seven cases (995, 1443, 2152, 2969, 3366, 2369, 2705) go
back to expecting the fitted thread and its volume.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A modelled thread never changes the size of the hole or shaft it is on
(#1018, #2853): one that isn't the thread's size is refused, naming the
size to use. The seven cases that expected the hole filled to the tap
drill or the shaft turned down to the major diameter (995, 1443, 2152,
2969, 3366, 2369, 2705) now expect that refusal, with its numbers worked
out from ISO 68-1 and the message in `refusal`. An opt-in fit option, if
it comes, gets cases of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs are compared with the golden's times, so a run sharing the machine between cases flags
slowdowns that are only contention (seen with --jobs 4: four cases two to ten times slower).
--jobs still overrides it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
Tiers: primitives; booleans in the degeneracy classes (coincident and coplanar faces, edge-on-edge,
vertex-on-face, tangent along a line and a circle, near-tangent gaps and overlaps of 1e-3 and
1e-6 mm, seam and pole crossings) and cuts that end in a single line (a knife-edge wedge, a V
groove and a cylinder that pinch the part in two, a hole tangent to a face, a notch flush with an
end); line-and-arc extrudes and revolves (partial, touching the axis); spline profiles, sweeps
and lofts; holes and tapped holes (blind, drilled, counterbore, countersink, M5-M8 taps, into a
curved face, near and tangent to an edge, crossing); modelled threads (external, fine, partial,
to a shoulder, internal, into a chamfer, at a filleted shoulder); fillets (constant, tangent
chains, convex and concave, variable down to 1e-3 and to 0 at a vertex, corners with equal and
unequal radii, g2 corners); chamfers (two distances, angle, three- and two-edge corners, curved);
shells and offsets; patterns and mirrors (overlapping instances that cut a core free); and
cumulative parts.
A running part grows one tier at a time through `base` (back wall, revolved boss and slot,
spline window, tapped holes, rounds, chamfers, shell, vents), and the last case adds its thread:
it carries every tier. Answers are analytic wherever the geometry has them (Pappus, Steinmetz,
ISO 68-1 basic thread profiles, exact Gauss quadrature of Bezier profiles, inclusion-exclusion
for chamfered corners), otherwise invariants; each `method` gives the derivation, and
tools/kernel-ladder/cases.py (standard-library Python, like tools/oracle-analytic) writes them.
Capabilities SolidCraft lacks are `missing` cases: multi-start threads, thread runout lengths,
fillet corner setbacks.
The golden is the first recording on truck, each tier sorted by its time. SMLib booleans are
not on this branch yet, so the smlib column records "not supported yet" for every case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
The ladder orders kernel operations by complexity instead of by random goldens: a manifest
(tests/kernel-cases/ladder/ladder.json) lists tiers in order and each tier's cases in order;
every case is graded against its own answer like any rig case, and a golden
(tests/kernel-cases/ladder/golden.json) records per case and kernel the status, the commands'
time and what the kernel made. A run compares with the golden and flags slowdowns (over twice the
time and 0.25 s more), status changes and changed counts. `--record REASON` re-measures one case
at a time, rewrites the golden with its reason and sorts each tier by the first kernel's time.
The report, docs/kernel-ladder.md, gives per tier and kernel the counts per status and the
frontier: the first case in ladder order the kernel fails. It reuses the rig's process-per-case
runner, timeout and cells; results now carry their path under tests/kernel-cases/ so cases from
other areas can be listed in a tier. The README documents `base`, `missing` and the ladder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
A revolved cone tip with an exact volume and area; auto-reports without
a design are listed in the skipped.json files.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A tray with seven rounded edges, both rim rounds of a tube, a chamfer on
a joined L-shaped body, and a circular pattern of a chamfered hole, each
with an analytic volume.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An M8x1 thread in a 7 mm hole, and three booleans of finely faceted
prisms (1000 and 1200 flat faces) with planar and curved partners, each
with a closed-form volume. Three auto-reports without geometry are
listed in skipped.json.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
`"outcome": "solid_or_refused"` takes a clean refusal, or else grades the result as usual. Two
bodies that touch along a circle only (#168, #3841) have no one-solid union: a refusal or the two
bodies left apart are both right; fusing them is wrong. The two cases said opposite things
(two bodies, a refusal) and now agree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `"shells": N` in expected.json grades the total number of shells: a body with a sealed void
holds two, and some joins keep several lumps in one body. Those cases (#3175, #3455, #3526,
#3730) are graded on validity again instead of being marked not valid.
- When SMLib's record conversion says "not supported yet" (revolved surfaces, traced intersection
curves), its check gives no verdict. That's shown but not graded, since it isn't a fault of the
body. solidcraft_kernel::SmlibCheck carries the three outcomes.
- `$OUT` in a recipe is a scratch directory outside the repository, for files a case writes. The
two STEP-export crash cases write there instead of /tmp.
- cargo xtask kernel-cases --results FILE keeps every result as JSON.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every other sweep, loft, revolve, pipe, coil and rib issue and PR, one line each: auto-filed reports that carry only command parameters, reports cut off in the source, UI and dialog items, feature requests, and failures that belong to import, shell, fillets, booleans or tessellation.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Coils split and trimmed by a plane, cut with a negative pitch, joined flush round a cylinder, intersected with a cylinder, cut as a thread groove into a bore, and placed with a square wire outside the diameter (screw-motion Pappus); surface revolve, sweep and loft sheets with areas worked out by integration, and a rail-guided surface loft in either section order.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
End faces of a loft, a sweep and a coil extruded, closed twisted sweeps round a torus, a twisted square cut through a tube, a ring swept along a short arc and joined to a collar, two overlapping swept arc walls, a rib grown along the sketch normal, and three pipes meeting at a point.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
End scale, parallel orientation and guide rails on straight paths (frustums and a sheared cylinder), a rectangle swept round a mitred corner, a profile mid-line on a closed rounded rectangle, second sweeps from a plane along a closed circle, hollow and cutting pipes, and a flat wall of a swept corner extruded.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refusals for a profile across the axis and an axis outside the sketch plane, a construction axis picked by name, a cone left by a revolve cut, revolve joins and cuts whose end faces lie in a face of the target, a dome cut by a ball on its seam, a horn-torus turn, and half-disc revolves with a refused hostile sweep scale.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Spherical shell sketched off the origin, adjacent profiles on the axis, whole planar faces of full and partial turns and a face pick on a fresh revolve, a half slot turned about its centreline (alone and as a cut), a torus cut by an overlapping ring, and a 300 degree revolve crossed by a plate.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Added: 451 (block inside / crossing a hollow spherical-shell tool), 1107 (hollow hub joined
with nine blades), 1140 (spiral hollow lofted tube cut in half and joined), 2928 (cap-touching
lips with 0.0001 mm offset walls), 3850 with 3741 (cross cut touching a round side, and the
hole after it), 1984 (near-coaxial pin and rivet overlaps). #3573 is now a source of
shell/3576. Their lines are gone from the skipped files; #2259, #3518 and #3602 stay skipped
with corrected reasons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hollow elbows lofted along a bend (Pappus), hollow bands halved and joined, a flange touching the end of a bent tapered loft, tapered and twisted lofted blades patterned round a disk, and a loft joined into a window that it overhangs, alone and mirrored on a hollow case.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A shallow loft cut from a slab top face, a loft cut whose sections touch the block sides given in either order, circles lofted into a cone on a turned-over plane, a loft guided by a rail, a circle lofted to a comb outline, a square lofted to a circle, and a circle-to-rectangle loft joined to a cylinder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ruled lofts of zig-zag outlines worked out with the prismatoid formula, rotated rectangle sections turning through half a turn (Pappus), ring sections with round and rectangular holes, a cone loft meeting a cylinder wall near its top, a loft standing on a box face, a loft cut limited by targets, and a refusal for sections in one plane.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The rest of the thread cases (holes at the nominal size, shafts and holes
off the thread's size, the crash and sketch-attachment repros) and the
triage of the sources that are not kernel geometry.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
External and internal modelled threads, tapped holes and threads beside
other features, each graded against the ISO 68-1 groove volume (Pappus)
and the part's own closed-form volume.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every other fillet, chamfer and blend issue and PR, one line each: auto-filed reports from private designs (picks and error only), sketch fillets and chamfers, UI, previews and navigation, feature and fit-option requests, reference cases that already exist in tests/oracle, surface modelling, and failures that belong to booleans, shell, import, tessellation, sheet-metal or threads.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The graded volume was right; the derivation text quoted the union as 46476.5 instead of 45571.7.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copies of filleted and chamfered features (a pasted block, a pattern of pockets with corner rounds, a pattern of chamfered bosses), a grille of rib roots, a join that needs a boolean retry followed by a fillet, and curvature-continuous fillets worked out from the documented quintic section.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Rounds along curved outlines, with the removed volume worked out by Pappus or by integration: merged cylinders, a D prism, a rounded rectangle with 0.02 mm end edges, a long box beside quarter turns, vertical corners after bottom rounds, a bent tube end, a boss's bottom arc, bars running into a boss, an edge ending on a curved wall, and the knife-edge arc corner.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Rounds with their own right answers: a radius larger than the ring it runs along (refused), a pick on a smooth seam (refused), a far edge pick (refused), a round that uses up an end face or a wall, a round over a hole, rounds on both sides of a thin wall and concentric with an inner one, a round on a sphere's flat cut, an edge ending on a tangent seam, and rims on B-spline ramps.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Chamfers on boxes, plates, discs and sectors with closed-form volumes: all twelve edges in the order that was refused, a concave chamfer that adds material, a chamfer next to square openings and one that runs into them, a hole rim tangent to the bottom face, a hex hole's corner bevels, a one-pick chain on a large part, a bottom loop as wide as the corner round, two distances on a circular rim, the end outlines of a rounded prism, a chamfer over a joined pin, and the equal chamfer of a slanted plug. Spline-sided pillar (invariants only) for chamfer and fillet.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Cases: mirror of a cut under a wall (#4011), coplanar intersect (#3671), multi-lump
intersect (#3526), split along a face plane (#3447), 64-peg join (#3455), boss on a rod
(#3485), collinear cylinders and pin in a bore (#3497), swept G2 cut (#3604), spokes
tangent to a hub (#3612), sphere and pyramid (#3633), column in a channel (#3665),
extrude to a point on an edge (#3729), emboss on a cylinder (#3782), circle-only join
(#3841), ring on a perforated plate (#3887), symmetric revolve cut (#4008), walls round a
floor (#4058), slab beside a bore (#4095), and others. The rest are skipped with a reason
in skipped-c.json.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every other sheet-metal-keyword issue and PR, one line each: UI, docs, file
format, naming and attachments, DXF output, feature requests, user designs
with no repro, and failures that belong to booleans, fillets, shell, sweeps
or threads.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Trays at 72 degrees and with four angles and heights, a tray shrunk by a
parameter and unfolded, an L bracket unfolded and refolded, opposite flanges
at 105 and 120 degrees, torn corners folded, and a rhombus whose square
reliefs would overlap.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Unfolded trays graded by the flat size W + 2(H - BD): torn, round and square
relieved corners, per-feature reliefs, rounded plate corners; a shelled tray
and a bracket converted at their bends; an L converted to the rule's radius.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A flange beside the leg of an L, along what a cut-out leaves of an edge,
beside a ledge as tall as its setback, a return on a flange's far edge, a
round hole, a slanted slot and an ellipse across a bend, a hundred slots with
four folds, and the refusal of rule values sheet.flange would ignore.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Cut-outs folded and refolded, a hole square to a 45 degree flange, a slot
that parts a flange from its base, an inside flange on a plate turned
62 degrees, cuts after cuts, a hole beside two adjacent flanges and a flange
on a flange's side edge; refusals for a flange on a bend line and a flat
pattern after a fillet; a mirrored sheet body that must stay sheet metal.
Volumes by hand (K = 0.5 folds: flat area * t; otherwise bend volumes).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plates converted to sheet metal (holes, long thin strips, engraving, a
channel with modelled bends, position kept) and holes and cut-outs
through or into a sheet; volumes by hand from the plate and the holes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>