Sheet 03 — casebook
Not one of them is a crash. A crash is the easy case: it announces itself, it has a stack trace, and nothing downstream is built on top of it. These are the other kind — where the kernel returned a confident, well-formed, wrong answer.
A phantom revolve that served the wrong geometry. A section that reported the uncut area. A marcher whose output was, in the source's own words, silent noise. A union that dropped two faces. A drill that reported four holes while removing no material.
Each is reproduced here with the verbatim source text, the exact figures, and the commit that closed it. Where a case is weaker than its reputation, it says so — including one whose documentation outlived the bug it described.
Case 01 — the phantom effect
The timeline is event-sourced: a model is rebuilt by replaying what happened to it. A
revolve was recorded as revolve_face carrying the kernel's internal
profile face id — a handle that is meaningful only inside the session that minted it.
On a forty-five-event log, one such id no longer resolved, and boot quarantined honestly. The other did something far worse: it resolved to an unrelated live face that happened to share the number, and the replay silently revolved the wrong geometry. A served event, a plausible solid, no error anywhere.
The fix is at the recording root, not in replay, and the reasoning is stated where the guard lives:
// Resolving `face_raw` by raw fallback would revolve whatever UNRELATED // live face happened to share that numeric id — a silent wrong-solid (or, // when the id resolved to nothing buildable, a phantom served event with no // geometry). Such an event is unreplayable by construction: fail LOUD as a // dangling reference so boot QUARANTINES honestly at it, never serves a // phantom.
Legacy events of that shape are now refused rather than migrated. There is no lossy upgrade path, because any upgrade would have to guess which face was meant.
#[error("dangling reference in {kind}: {entity} no longer resolves")]
DanglingReference { kind: String, entity: String }
The regression test does not check that replay fails. It checks that replay fails
for the right reason — seeding a box that owns faces 0 to 5, then replaying a
revolve_face naming face 3, which would otherwise succeed by coincidence.
/// Mutation proof: delete the `id_remap.contains_key(&face_raw)` guard in /// the `revolve_face` arm and this regresses — the kernel revolves the box's /// face 3 (a coincidental success, or a `KernelError` that is not `Dangling`)
"durability: QUARANTINE — the log contains an
event this kernel cannot faithfully replay;
serving the clean prefix and refusing the
tail. is_sound={}"
Case 02 — the confident wrong area
Sectioning a cross-drilled block through both bore axes returned a number larger than the undrilled block — 4000 mm² where the solid rectangle is 2400 and the true cruciform is 1056. It also drew a phantom diagonal edge across the void.
What makes this case instructive is that the code was not wrong when it was written. A tier-one bounding-box fast path assumed analytic faces are never boolean-trimmed. That held — until booleans began trimming analytic faces, at which point a cross-drilled bore wall started carrying the other bore's Steinmetz curve as a trim boundary.
the tier-1 bbox fast path assumed analytic faces are never boolean-trimmed; a cross-drilled bore wall carries the other bore's Steinmetz curve as a trim boundary, so section generators ran straight across the drilled-away void and chained into a self-overlapping loop
Two further defects sat underneath: the winding test broke on full-wrap trim loops, and the seam force-include bypassed the hole test entirely. The two Steinmetz scallops, joined at their tangency points, form a single closed loop whose lifted polygon is not closed in the universal cover — so the membership test mis-unwrapped it.
The fix does not repair that winding test. It routes around it, into an even-odd membership test in quotient parameter space, behind a new soundness gate that asks whether the face's UV domain is actually rectangular before taking the shortcut. The real content of the fix is checking the assumption instead of inheriting it.
The assertion carries the diagnosis in its own failure text:
"axis-plane cruciform area {area:.2} vs analytic {expected:.2} (rel {rel:.4});
caps={} — full-rect 2400 means the void was not subtracted at all"
Case 03 — the marcher that never marched
Surface–surface intersection for pairs with no closed-form solution falls back to marching: predict along the tangent, correct back onto both surfaces, repeat until the curve closes. The boolean engine carried its own copy of that marcher, and the copy had two problems that compounded.
The first is arithmetic. The step size is tied to the distance tolerance —
tolerance.distance() × 10, which with a 1e-6 tolerance gives a step of
1e-5 world units. The hard cap is 200,000 steps. So the
reach of a single trace is two world units per direction. A Ø100 ring has a circumference
near 314. The marcher could traverse about two per cent of it before
hitting the cap.
The second is what happens at the cap. The partial trace is discarded with
Ok(None) — a return value indistinguishable from "these surfaces do not
intersect". And because the seed grid samples 0..=20 in both parameters,
441 candidate seeds feed the loop, each one re-running the same doomed
two-per-cent trace, because this copy of the marcher had no seed-consumption array.
// HARD STEP CAP per direction. The marching step is tied to the distance // tolerance (≈1e-5), so a curve on unit-scale primitives needs ~10^6 steps; // for a pair with NO analytic SSI arm (cone×cylinder, cone×sphere, …) the // march can also fail to ever close → a true HANG that freezes the kernel // (the worst failure class).
The cap was added as an explicit safety measure, and the commit that added it refused to overstate what it had done: "This stops the freeze; it does NOT make cone∘cylinder geometrically correct (the marched curve is discarded)." A hang became a wrong answer, knowingly, as the lesser evil — and it was labelled as such.
A separate commit hardened the canonical marcher — adding alternating projection, curvature-adaptive stepping, periodic seam wrap, one curve per branch, citing Patrikalakis and Maekawa. Its diagnosis of the original defect is the best sentence in this casebook:
The general surface-surface marcher (the freeform fallback the analytic QSIC producers gate ahead of) did not actually march. correct_to_intersection took the predicted point but ignored it (_predicted), re-projecting from the anchor's own (uv1,uv2). The anchor already lies on the intersection, so the corrector returned the anchor unchanged and every trace collapsed to one repeated point. The analytic paths masked it; the marcher output was silent noise.
That commit touched the canonical implementation and one new test file. It did not touch the boolean engine's copy — which is precisely why the copy was still marching two per cent of a ring, 441 times, a year later.
Measuring rather than assuming turned up a second finding: the canonical implementation is also geometry-blind. A nominal chord of 0.01, a maximum step factor of 8 and a cap of 4,000 steps give a scale-independent maximum reach of 640 world units — regardless of how large the feature is. On a circle of circumference 2,720.699 it traced 639.660 and stopped. Not a bug in the tracing; a constant that assumed a scale.
The fix derives the chord from the geometry instead: the bounding diagonal of the seed cloud divided by 64, floored at the tolerance. Both scales now trace with the same sample count, and the test binary went from 11.34 s to 0.02 s. Separately, the step budget was hoisted to be per surface pair rather than per seed, so an unmarchable pair costs one budget instead of one per seed — 1.2 million evaluations where it previously cost about 24 million.
The obvious next move is to delete the duplicate and call the canonical one. That was attempted, and it broke a shipped watertightness test — a frustum union that had been watertight came back with 198 boundary edges.
The reason is worth sitting with. That test passes today because the marcher is broken. Its twenty seeds all hit the step cap, the tracer returns no curves at all, and the part is then built correctly by an entirely different route — the coincident-rim weld path. Give it a working tracer and it produces a curve fragment that the imprint pipeline then mishandles into a doubled edge.
The first diagnosis was that the fragment lay exactly on the shared rim, and that the imprint pipeline failed to recognise a partial arc as boundary-coincident. That framing was published on this page. It was wrong, and measuring rather than reasoning is what caught it.
Instrumenting the coincidence predicate gave the fragment's actual residual against the rim: 3.948 × 10⁻², where rim-coincidence would be around 10⁻⁷. The fragment is not on the rim. It is 0.04 away from it.
The reason it is 0.04 away is the real finding, and it is a soundness problem in its own right. The marcher's step law targets a fixed turn per step — 0.2 radians — which fixes the chord at a constant fraction of local radius, roughly half a per cent. That is a shape-fidelity criterion. It is scale-free, which sounds like a virtue, and it is entirely independent of the distance tolerance the caller passed in.
So at a radius of 6, handed a tolerance of 10⁻⁶, the marcher returns a polyline sitting 0.03 to 0.04 away from the true curve — about thirty thousand times coarser than requested — and says nothing. Every consumer downstream believes it received geometry accurate to the tolerance it asked for. The marcher does not honour its own contract, and that only became visible when unification pointed its output at a pipeline that actually requires tolerance-accurate input.
The principled fix is to bound the chord by the caller's tolerance rather than by turn angle. It was implemented, and it is correct and bounded on the original case. It also makes the frustum union hang — no completion in fifteen minutes — for reasons not yet diagnosed. It was reverted rather than left in the tree.
So: one real imprint defect found and fixed along the way, a wrong root cause corrected by measurement, the true root cause identified and its fix deferred because it exposes something worse. The duplicated marcher still exists. All of that is written down as debt rather than rounded up into a claim of completion.
763e9e16. The step
size and budget are fixed; the duplicated implementation is not, and the reason is in the
body — removing it regresses a test that passes only because the marcher is broken.
Case 04 — idempotence
If B is contained in A, unioning them should return A unchanged. It did — once. Applied a second time, with the operand's faces coincident with the earlier union's imprint, it produced an open shell: four edges used exactly once, where a closed solid requires two.
The cause was a face that vanished quietly. Region extraction returned zero loops, the fragment loop therefore iterated nothing, and the only short-circuit that would have preserved the face fired earlier in the pipeline:
split_face_by_curves silently DROPPED any face whose post-arrangement extract_regions returned 0 loops (the fragment loop iterates nothing; the only unsplit short-circuit fires pre-arrangement). Trigger: a repeated coincident- contained union whose re-imprint cuts diverge in vertex identity (the chamfer's corner-clip) -- both coincident +X caps hit 0 loops and vanished; Union's OnBoundary from_a-only rule dropped B's twin -> open shell (edges 1,5,9,10).
The chamfer is what made it reproduce: its corner-clip caused the re-imprint to diverge in vertex identity, so the second union's cuts were not recognised as the same cuts. A control case without the chamfer does not reproduce, and is kept as a documented control rather than deleted.
The fix is a post-arrangement fallback — zero extracted loops with active cuts means preserve the face unsplit — and it was mutation-proven: disabling the fallback returns the identical open shell.
The test's own doc comment still says it is left #[ignore]d and instructs
the reader to run it with --ignored. That is no longer true: the attribute is
gone, the test runs, and the defect was fixed two and a half hours after the repro was
banked. The comment outlived the bug it described. Documentation rot in a
codebase whose thesis is that it cannot lie is worth printing rather than quietly
correcting.
Case 05 — two layers agreeing on a lie
A bolt pattern on an off-origin part was drilled around the world origin. The agent received "holes: 4" and a sound certificate, and the volume never changed.
It took two independent failures to produce that, one on each side of the language
boundary. The tool schema had no center parameter, so the argument was
silently stripped before the request was ever sent — the structural-typing
default that exists as a safety feature, acting as a data-loss channel. And the kernel, on
receiving a difference whose tool never touches the target, returned a copy of the target
as 200 and SOUND.
The second half is the one that matters here, because the first is a schema bug and the second was a philosophy bug. A cut that removes nothing is now a typed refusal:
"Difference tool does not intersect the target: the operands are disjoint, so the cut would remove nothing (target returned unchanged is not a result this kernel reports as success)"
The gate is deliberately narrow, and the narrowness is the interesting engineering. It fires only when faces were selected and neither operand contributed contact — so annihilation, where A is wholly inside B, still falls through to an empty result, and enclosed voids keep B's faces and never trip it.
if matches!(operation, BooleanOp::Difference) && !selected_faces.is_empty() {
let any_contact = intersections.iter().any(|fi| { !fi.curves.is_empty() || ... });
let tool_contributed = selected_faces.iter().any(|f| f.from_solid == solid_b);
if !any_contact && !tool_contributed {
return Err(OperationError::DisjointDifference);
}
}
Mutation proof, from the commit: hardcoding the cylinder centre back to the origin goes red — caught by the new refusal rather than by the geometry.
Case 06 — the institutional response
The five cases above share a shape: the kernel had something true to say and said something else. The response was not more tests. It was making refusal and uncertainty into first-class types, so that the honest answer is expressible.
Geometric tolerance verification returns a three-valued verdict, and the reasoning is in the type's own doc comment:
/// The verdict of a conformance check. A tri-state, never a bare bool, so an /// unimplemented characteristic is reported honestly rather than as a pass. /// /// The kernel does NOT yet verify this characteristic/feature combination. /// Carries a machine-readable reason. Never substituted by a pass.
The measurement accessor returns an option, not a boolean — None when
nothing was measured. An unimplemented check cannot silently read as conformant, because
the type has nowhere to put that lie.
"Edges {} and {} share corner vertex {} with MIXED convexity (a convex blend
edge and a concave blend edge meet here). …no single sphere/plane is tangent
to both convexities. Mixed-convexity corners require an N-sided Gregory /
GB-patch corner blend (Charrot-Gregory 1984; Vaitkus-Várady-Salvi GB-patches,
CAGD 2018) — not yet implemented (Task #82 / F5)…"
It names the entities, the geometric reason it is impossible with the current machinery, the literature that solves it, and the task tracking it. An agent can act on that; so can a person.
The message above used to be correct but misleading: a mixed-convexity corner fell through to a generic branch that called it a same-kind corner patch and recommended a workaround which does not apply. The operation failed either way. The commit changed nothing about what the kernel can do — only about what it says — and closed with the line that is the thesis of this entire page:
An honest refusal must also be an accurate one.
A blend refusal once advised applying each edge in a separate call. A later gate established that this is exactly the sequence that corrupts the solid. The advice had been recommending the corruption:
"Do NOT apply these edges in separate single-edge calls — sequential single-edge blends at a shared corner are unsupported (they corrupt the solid) and are refused."
Two more worth naming: OpTimeout, which states that "the model is
unchanged" because the operation ran on a discarded clone; and the pair
EmptyResult and DisjointDifference — the same observable symptom
split into two typed causes, "the answer is nothing" and "the cut would change
nothing."
"operation '{op_kind}' exceeded its
{budget_secs}s time budget and was aborted;
the model is unchanged"