kernel-cases: an outcome for geometry with no single manifold answer

`"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>
This commit is contained in:
bherbruck
2026-10-21 06:40:28 -04:00
committed by jyviko
parent 678a70da2c
commit c24b066d25
4 changed files with 23 additions and 16 deletions
+9 -4
View File
@@ -81,10 +81,15 @@ fn check_case(dir: &str) -> Value {
out["error"] = json!(format!("this build runs booleans on the {running} kernel, not {}", kernel()));
return out;
}
let refused = want["outcome"].as_str() == Some("refused");
// `refused`: only a clean refusal is right; `solid_or_refused`: a clean refusal, or a result
// graded as usual (where the geometry has no manifold answer, such as bodies touching along a
// curve only).
let outcome = want["outcome"].as_str().unwrap_or("solid");
let refused = outcome == "refused";
let may_refuse = refused || outcome == "solid_or_refused";
let mut s = solidcraft_engine::Session::default();
let ran = s.run_script(&commands);
match (ran, refused) {
match (ran, may_refuse) {
(Err(e), _) if crashed(&e.to_string()) => {
out["status"] = json!("crash");
out["error"] = json!(e.to_string());
@@ -102,12 +107,12 @@ fn check_case(dir: &str) -> Value {
out["error"] = json!(e);
return out;
}
(Ok(_), true) => {
(Ok(_), true) if refused => {
out["status"] = json!("fail");
out["error"] = json!("succeeded where the case expects a refusal");
return out;
}
(Ok(_), false) => {}
(Ok(_), _) => {}
}
// Every body in the world, imported components included.
+1 -1
View File
@@ -75,7 +75,7 @@ parts): every issue and PR looked at that did not become a case, with one line e
| `what` | One line: the geometry that went wrong. |
| `source` | `analytic` (numbers worked out by hand; `method` gives the formulas) or `invariants` (no number is known: only the outcome, validity and counts are graded). |
| `method` | How the numbers were derived, or which invariants hold and why. |
| `outcome` | `solid` (default: the script succeeds) or `refused` (the right answer is a clean error, never a panic or a hang). |
| `outcome` | `solid` (default: the script succeeds), `refused` (the right answer is a clean error, never a panic or a hang), or `solid_or_refused` (either a clean error or a result graded as usual, where the geometry has no single manifold answer, such as two bodies touching along a curve only). |
| `valid` | Default `true`: every body is one closed shell (or `shells` in all) that passes the record's structure and geometry checks and, in builds with SMLib, SMLib's `check(All)`. `false` only where the result is legitimately not a solid (a sheet). Where SMLib can't take the record yet (a surface kind its conversion lacks), its check gives no verdict and is not graded. |
| `shells` | The total number of shells, where it isn't one per body: a body with a sealed void (2), several lumps kept in one body. |
| `body_count`, `faces`, `edges`, `vertices` | Exact counts, where the geometry fixes them. Faces and edges count whole analytic surfaces and curves once (a full cylinder side is one face). |
@@ -1,10 +1,11 @@
{
"sources": [
"tangency-labs/solidcraft#168"
],
"what": "a rim on a coin's top face that meets the coin along one circle only (no shared area, no shared volume): it must not be joined into it",
"source": "analytic",
"method": "coin r10 h5: 500 pi; rim = ring r10..14 standing on z=5 for 3: pi*(196-100)*3 = 288 pi. They share only the circle r10 at z=5, so the result is two bodies (or a refusal, never one joined body); volume 788 pi = 2475.664. body_count 2",
"body_count": 2,
"volume_mm3": 2475.5750110287568
"sources": [
"tangency-labs/solidcraft#168"
],
"what": "a rim on a coin's top face that meets the coin along one circle only (no shared area, no shared volume): it must not be joined into it",
"source": "analytic",
"outcome": "solid_or_refused",
"method": "coin r10 h5: 500 pi; rim = ring r10..14 standing on z=5 for 3: pi*(196-100)*3 = 288 pi. They share only the circle r10 at z=5, so the right answer is a clean refusal or two bodies, never one joined body; volume 788 pi = 2475.664. body_count 2",
"body_count": 2,
"volume_mm3": 2475.5750110287568
}
@@ -5,6 +5,7 @@
],
"what": "Join of a ring (r10..15) onto a coin (r10) that touch only along a circle: no fusion that moves the ring",
"source": "invariants",
"outcome": "refused",
"method": "the bodies share one circular edge and no area, so a join has no valid solid to give: the right answer is a clean refusal (New Body gives two independent solids). Succeeding means the kernel moved or rotated the ring to fuse it"
}
"outcome": "solid_or_refused",
"method": "the bodies share one circular edge and no area, so a join has no single manifold solid to give: the right answer is a clean refusal, or the two bodies left as they are (body_count 2). Fusing them means the kernel moved or rotated the ring",
"body_count": 2
}