Status: accepted (2026-07-29)
Extends ADR-0019 (certified intervals) and
ADR-0021 (one canonical B-rep). Supersedes nothing:
the exactness charter is unchanged, and this is an application of the escape
hatch ADR-0019 already built rather than a loosening of it.
Restates ADR-0021's surface/trim schema, which had drifted from the code
(see ADR-0021's data model below). ADR-0021's decision — one canonical B-rep,
representations as constructors — is untouched.
The capability matrix scores a cell green when one representative solid
meets one representative operation. It has never asked what fraction of the
parameter space behind that cell answers. gitcad.bench.coverage now does:
| family | parameter coverage |
|---|---|
| face off a box (planar control) | 77/77 — 100% |
| cap on a sphere (K2.x rung 2) | 33/33 — 100% |
| drill a plate, bore fully inside | 41/41 — 100% |
| pocket into a bar | 39/39 — 100% |
| flat on a bar (K2.x rung 1) | 3/33 — 9% |
slot through a bar — the composed grid's own cut box shape | 0/25 — 0% |
| bore across a plate wall (the 17-cell family) | 8/33, all 8 non-crossing; the crossing set is 0% |
The discriminator is whether the cut leaves a circular SEGMENT. Not which
quadric is involved, and not which curve the two surfaces meet in. A full disc
removed, a polygon pocket, and a plane cutting a sphere all have polynomial
closed forms and answer everywhere. A chord across a disc leaves
A = r²(t − sin t cos t), t = arccos(h/r)
and by Niven t is a rational multiple of π only at twelfths. The admissible
set is finite inside a continuum. K2.x rung 1 is a green cell on the matrix and
answers a machinist at three depths on a Ø10 bar.
Every remaining boolean family in the composed grid is segment-producing.
Continuing to build exact rungs therefore closes cells on the matrix while
shipping capabilities that refuse on essentially every real input — and the
matrix, counting cells, would report progress the whole way. Two earlier
roadmap plans were already killed by measuring the denominator
(docs/kernel-improvement-backlog.md §2); this is the third and the most
expensive to have got wrong, because the work would have looked like it was
succeeding.
body._band_sweep returns a swept angle in quarter turns — an integer
index — and _quarter_antiderivative is indexed the same way. Trimmed
cylindrical, conical and spherical faces are measured by counting sectors,
not by carrying an angle. That representation is what pins every arc to
twelfths, and notch.py already records the consequence: a face whose angular
span is not a whole number of sectors "would need a new quadric measure that
500+ existing faces also depend on."
So this is not a routing job inside one rung. It is a change to how a trimmed
quadric face states its extent.
ADR-0019 introduced certified intervals as a K3 need: "K1 and K2 hold to a
hard rule … K3 breaks that comfort." That rule is still literally true — K2
uses no floats, it refuses — but it framed exactness as settled for K2, and
the coverage sweep says otherwise. K2's failure mode is not the one ADR-0019
anticipated. It is not "the quantity cannot be represented at all"; it is
"the quantity can be represented only on a null set", which reads as
success on a cell-counting instrument and as refusal to every user.
ADR-0019 needs no revision for this. Its governing consequence —
The exact fields remain the default and the first choice; CInterval is used
only where no exact field reaches. A model that could be exact must not
silently fall back to an interval.
— is exactly the constraint this ADR must honour, and is why the exact sector
path stays primary and bit-identity is the acceptance criterion below. What
changes is only the reach of the escape hatch, from K3 to K2.
**Carry the angular extent of a trimmed quadric face as a certified angle (an
ADR-0019 CInterval) rather than an integer sector index, and let the exact
sector path remain as the fast, exact special case it already is.**
(k0, span) or a certified [θ0, θ1]. Where theendpoints land on twelfths the exact path is taken and the result is
provenance: "exact", bit-identical to today.
meets the circle at (h, ±√(r²−h²)), which is already representable in
ℚ[√d] for any rational h — and the measure is certified.
mass_props reports provenance: "certified" with a proven half-width, as it already does for TubeSolid and TrimmedShell. A certified result is
never silently mixed with an exact one.
Where a bracket straddles zero the kernel tightens and retries, and refuses
if it cannot certify within budget. It never guesses.
The missing primitive is already built: forge f61e2aa adds certified
arccos, sin and cos, with the enclosure verified against identities
rather than against a double (the brackets are ~1e-40 wide; a float oracle
carries ~1e-16 of error and cannot witness them).
A bare float carries no proof — you cannot tell a correct 534.6435 from a
rounding artifact, which is the argument ADR-0019 already made and this ADR
does not reopen. Moving to tolerance-based arithmetic would be a different
decision, would supersede ADR-0018/0019, and would give up the property that
distinguishes this kernel: an agent can act on "certified ± ε" and on a
refusal, and cannot act on a silent wrong number.
capability._EXACT_FIELD_BOUNDARY marks 4 single-op and 10 composed cells as
outside any exact field, permanently — a square prism through a sphere
(arcsin), through a cone (ln(1+√2), by Baker), a fillet's turn angle
(arctan, by Lindemann), a loft's sweep (arccos(1/√37), by
Gelfond–Schneider). Every one of those constants is transcendental and every
one is bracketable. Under this ADR they stop being permanent walls and become
certified answers — 14 cells that the roadmap currently excludes from its
own denominator.
That is a second correction to the same dict, which already carries one: the
(chamfered box, chamfer(all)) entry was labelled permanent and was merely a
field limitation. A "permanent" label has now hidden closable work twice.
ADR-0021 records the canonical B-rep as `Surface ∈ {Plane, Cylinder, Cone,
Sphere} and Curve ∈ {Line, Circle}. That had already drifted: Torus` is
absent from the ADR, Sphere is SphereS, and — the part that matters here —
the model says nothing about how a face states which part of its surface it
occupies, although two surfaces already carry that on themselves. As accepted,
the schema is:
Body = [Face]
Face = (Surface, [Loop], sense) # sense: outward normal == surface normal?
Loop = [Edge] # first loop outer, rest are holes
Edge = (Curve, v0, v1) # v0 == v1 for a full circle
Surface ∈ { Plane(n, d)
, Cylinder(p, d, r)
, Cone(p, d, tan_half)
, SphereS(c, r, pole) # pole: TRIM — which side of the one rim
, Torus(c, d, R, a, k0, span) # (k0, span): TRIM — sector extent
}
Curve ∈ { Line(p, d), Circle(c, n, ref, r) }
A trim is carried on the surface, not inferred from the loops, because one
rim bounds a quadric in two ways and the loops cannot say which (ADR-0021's own
_sphere_zone note records the same ambiguity for a two-rim sphere face). This
ADR keeps that rule and changes only the number kind a trim may hold:
trim extent ::= (k0, span) # exact, integer sectors — unchanged
| CInterval [θ0, θ1] # certified, this ADR
The exact form remains the default and the only form used where the endpoints
land on sectors, per ADR-0019's rule that a model which could be exact must
not silently fall back to an interval. Adding the second form is the whole
technical content of this ADR; everything else follows from it.
Geometry output changes for every segment-bearing cut, so by CLAUDE.md rule 3
this is a breaking change, never auto-merged, and by ADR-0006 it is
Tier 1 at best. It follows the major-change process in CLAUDE.md:
result must stay bit-identical, which is the acceptance criterion, not
a nice-to-have;
tests/invariants/ and tests/golden/ stay green throughout;thousands of operations where an index lookup was one. The exact path
staying primary is what keeps this off the common case.
provenance becomes load-bearing for consumers. Anything that compares two mass_props volumes for equality must handle brackets, and a certified
value must never be written into a byte-canonical document as though exact
(ADR-0004).
sector index cannot simply be deleted — it is what makes the 100% families
exact and fast.
The segment-bearing families move from single-digit parameter coverage to
total coverage, and the composed grid's remaining boolean gaps stop being a
list of rungs that cannot be finished. gitcad.bench.coverage is the
acceptance instrument: the number to move is the percentage, not the cell.
Following the CLAUDE.md major-change process. Steps 1–2 done (this ADR; the
loop is at Tier 0 for kernel semantics by rule anyway). Step 3 in progress:
f61e2aa — certified trigonometry (arccos, sin, cos),with the enclosure verified against identities rather than a double, since
the brackets are ~1e-40 and a float carries ~1e-16.
15db93b — the arc-span foundation: arc_cos_sin (exact at any angle), arc_span_certified, and certified_bracket (a float proposes,
exact comparison disposes). Validated against _arc_quarters across all 84
start/span combinations on the twelfth grid.
every rational depth: 0 unpaired edges at h = 1, 5/2, 3, −1, 7/3 on a
radius-5 bar. So only the MEASURE is missing, not the construction. This is
the fact that makes the rest tractable.
4f35f01 — the measure, and **rung 1 is 33/33 (100%) in the parameter space**, from 3/33. _needs_certified routes before any arithmetic
runs, so on-grid bodies stay byte-identical. The exact per-face term is tried
first and bracketed, so new arithmetic runs only where no exact term exists;
all 15 corpus representations agree exact-vs-certified. vector_area_certified
keeps the orientation oracle at full strength — the only one of the audit's
four checks that sees a reversed face — and its test inverts correctly, since
a certified zero is a bracket containing zero.
seam as NaN with centroid_unavailable (the TrimmedShell precedent; a
Monte-Carlo check confirmed the flag is the honest answer rather than a wrong
number). The bore-across-a-wall family is unchanged at 24%, because it
refuses inside DrilledSolid's wall check rather than here; the slot
family moved 0% → 4%. Each needs its own wiring.
arccos to 1e-40 costs ~133 exact-rational cos evaluations with compounding denominators: one flat
volume took 51 seconds. A float proposing and exact arithmetic disposing
needs two, and with memoisation (~80 calls per body for ~12 distinct angles)
and 22 series terms instead of 30 it is 24 ms.
math.acos, so thefloat's own error is the floor — requesting 1e-25 fails verification and
settles near 1e-16 anyway. The default is the achievable 1e-15, about 1e-13
relative on a bar's volume: comparable to a double's error but proven to
enclose. Tighter costs bisection and is available via width=.
_centroid_offgrid gives the certified familiesa centre of mass, not just a volume. The band moment carries the (D/2) term
the on-grid formula drops (on-grid arcs are quarter-turns, D=0; a flat's arc
is not), Monte-Carlo verified to the noise floor. So rung 1 is now a
complete answer through the seam: certified volume + real centroid.
arcsin, ln, arctan, exp certified, which turn _EXACT_FIELD_BOUNDARY's transcendental "walls" (arcsin, ln(1+√2),
arctan) from permanent refusals into bracketable answers. **The arithmetic is
built; the geometry is not** — closing those cells still needs the
sphere-minus-prism and cone-minus-prism bodies constructed (trimmed
spherical/conical patches, arc-trimmed walls), which is SSI-adjacent work.
revolve cut by half-space closed (single-op 353→354): a straight-walled revolve is coerced to a Cyl so the flat recognises it.
The measure and its primitives are the bounded part of ADR-0023 and are
largely done. The composed grid is still 180/285 because closing those cells
needs the bodies built, not just measured, and that splits into work of very
different cost:
bodies (trimmed quadric patches) do not. Bounded but substantial per family.
Body — needs the general boolean written once against Body, which is the ADR-0022 isinstance-chain migration (still 175
chains in ref.py, to move op-by-op under the differential oracle).
cylinder×cylinder intersection is a space curve with no elementary form. This
is where "certified" and "sampled" (ADR-0019's proposed third tier) diverge.
algorithms, multi-session, and K5.2 is the roadmap's largest single block.
Keep building exact rungs. Rejected by measurement: the next three
candidate rungs are at 0%, 0% and 9%.
Bare floats with tolerances, as OCCT and Parasolid use. Rejected — it
supersedes the charter rather than extending it, and it removes the one
property this kernel has that they do not. If it is ever wanted it should be
argued on its own merits in its own ADR, not arrived at by drift.
Facet the curved faces and measure the mesh. Rejected; ADR-0019 forbids it
and it converts an exactness problem into an unbounded-error problem.
Leave the rungs refusing and route users to the certified K7 path. Rejected
as a non-answer: K7's PatchSolid reaches these shapes only through a
freeform conversion that does not exist for Cyl, and a D-shaft should not
require a NURBS detour.