Thrown by runClash when a tolerance — either the run-level
ClashSettings.tolerance or a per-rule ClashRule.tolerance — is not a
finite number (NaN, +Infinity, -Infinity).
?? (the substitution runClash uses for an omitted tolerance) only
triggers on null/undefined, not NaN, so a NaN tolerance — e.g. from
Number('')/Number('abc') on a cleared UI input, or a unit-conversion
division by zero upstream — used to sail through unchanged. It then poisons
Math.max(tolerance, rule.clearance ?? 0) (NaN on any Math.max operand
is always NaN), which poisons the broad-phase AABB inflation
(inflate(bounds, NaN)), which fails every BVH overlap comparison, which
yields zero candidate pairs — silently, with the run still reporting
classifyRuleCoverage() === 'clean' because matchedA/matchedB are
selector-match counts taken before any geometry ran (#4244). Rejecting the
non-finite value at the door, before it can reach Math.max, makes that
whole chain structurally unreachable rather than merely harder to trigger.
A NEGATIVE finite tolerance is unaffected by this check and keeps its
existing, correct behaviour: Math.max(tolerance, clearance ?? 0) clamps
it to at least the clearance (or zero), same as before.
Thrown by runClash when a
tolerance— either the run-levelClashSettings.toleranceor a per-ruleClashRule.tolerance— is not a finite number (NaN,+Infinity,-Infinity).??(the substitutionrunClashuses for an omitted tolerance) only triggers onnull/undefined, notNaN, so aNaNtolerance — e.g. fromNumber('')/Number('abc')on a cleared UI input, or a unit-conversion division by zero upstream — used to sail through unchanged. It then poisonsMath.max(tolerance, rule.clearance ?? 0)(NaNon anyMath.maxoperand is alwaysNaN), which poisons the broad-phase AABB inflation (inflate(bounds, NaN)), which fails every BVH overlap comparison, which yields zero candidate pairs — silently, with the run still reportingclassifyRuleCoverage() === 'clean'becausematchedA/matchedBare selector-match counts taken before any geometry ran (#4244). Rejecting the non-finite value at the door, before it can reachMath.max, makes that whole chain structurally unreachable rather than merely harder to trigger.A NEGATIVE finite tolerance is unaffected by this check and keeps its existing, correct behaviour:
Math.max(tolerance, clearance ?? 0)clamps it to at least the clearance (or zero), same as before.