gitcad / docs / adr / 0020

ADR-0020 — forge is the sole geometry kernel; OCCT is removed

Status: accepted (2026-07-24)

Supersedes the OCCT-fallback and the OCCT leg of the three-oracle chain in

ADR-0018. The exactness charter (ADR-0018/0019)

stands unchanged.

Context

ADR-0018 introduced forge (the native, exact-arithmetic B-rep kernel) behind

the Kernel seam and kept OCCT (cadquery-ocp) for two jobs:

  1. 1. A jump-start / fallback — build the operations forge had not yet

implemented, so the product worked before the kernel was complete.

  1. 2. An independent oracle — the differential half of the three-oracle chain

(ref ⇄ OCCT ⇄ Rust), cross-checking forge's answers against a

30-year-hardened kernel.

Both jobs have largely retired themselves:

surfaces and complete-branch SSI, offsets, blends, lofts, exact mass

properties, tessellation, and native STEP read/write (K1–K7). A default

install already runs forge-first (ADR-0018 promotion, wired in get_kernel).

hidden-line removal (HLRBRep) behind 2D drawings — now replaced by native

forge HLR + section curves (forgekernel.hlr).

yet reach (it was the AutoKernel fallback for source-free ops, and the

forced backend for a handful of drawing/import sites). Cutting it converts

those from "works via floats" to an honest, stage-named refusal. That

capability delta is real and is enumerated below rather than hidden.

Meanwhile OCCT carries real costs: a large LGPL-2.1 binary wheel (install

weight and a license obligation), C-level stdout that corrupts the stdio MCP

stream, and a second geometry representation to convert to and from. Keeping a

whole kernel for one feature is the wrong trade once forge can do that feature.

Decision

Remove cadquery-ocp. forge becomes the sole geometry kernel.

  1. 1. Native forge HLR + section curves. Add hidden-line removal and section

curves to forge (forgekernel.hlr): exact straight edges for planar solids

(logical_edges), sampled rim circles + silhouette generators for cylinders,

and sharp + view-dependent silhouette edges from the tessellation for the

rest; occlusion by a hole-respecting inside-classifier (planar parity,

drilled = base-parity AND-NOT bores, else tessellation parity). section_polys

returns the cut-plane ∩ solid-surface curves in the same sheet frame, so a

section view's outline overlays its projection exactly. Visibility is a

display property, not a topological decision, so — under the exactness charter

— it may use floats; the geometry it draws is still the exact solid. The

drawing engine consumes projected 2D segments from the kernel seam rather

than calling any kernel-specific projector.

  1. 2. get_kernel is forge or nothing. The default is RefKernel (forge);

the only fallback is NullKernel (structure-only, honest about not checking

geometry). OcctKernel and AutoKernel are deleted; require="occt" is

removed.

  1. 3. The oracle leg retires. forge-vs-OCCT differential tests are converted

to forge-native invariants or deleted. forge's correctness now rests on the

three checks it already carries and controls:

certified-residual, and additivity/partition identities.

  1. 4. STEP is forge-native (already true via forgekernel.stepio); no OCCT in

any import/export path.

Consequences

~hundreds-of-MB LGPL wheel; the whole stack is Apache-2.0 + the exact kernel.

to leak onto the JSON-RPC stream (the redirect stays as defense-in-depth).

against a foreign kernel is gone. Mitigation is (3) above — invariants, the

ref/Rust cross-check, and fuzzing are internal but decidable and do not

depend on trusting a float answer. New exact-math still gets an adversarial

review pass (the established practice).

curved silhouettes and circular edges are polyline-approximated at the

tessellation deflection. Good enough for engineering drawings now; an exact

per-primitive edge path (clean arcs/ellipses) is a later refinement, not a

blocker.

cannot be reforged is dropped explicitly (with rationale) rather than kept

alive by an optional dependency.

Capability delta (what regressed to an honest refusal)

These features worked via the OCCT fallback and now raise a stage-named

NotYetImplemented until the forge stage that owns them lands. Each has a

golden contract preserved in-tree behind @pytest.mark.forge_gap, so it

lights up automatically when forge closes the gap — no coverage is silently

lost, it is quarantined and named. **The 0.9.x patch line restores these one

exact, adversarially-reviewed step at a time; the "restored" rows below are

already back.**

FeatureForge stageStatus
STL export / 3D view of a drilled solidK2.x DrilledSolid tessellationrestored 0.9.1
3D view / STL of bare cylinder / sphere / coneK2.x primitive tessellationrestored 0.9.1
circular pattern, tilted / diagonal sketch planesK2.2 exact ℚ[√d] rotationrestored 0.9.2 (angles that are multiples of 30°/45°)
chamfer of a selected edgeK1.2 stable edge-id chamferrestored 0.9.3 (axis-aligned box edges, exact planar wedge)
counterbore holes (coaxial cyl subtraction)K2.0 drilled solidsrestored 0.9.3 (sequential coaxial cuts)
countersink holes (cone subtraction)K2.2 conical bore cutforge_gap
engrave (text-pocket boolean)K3.1 arbitrary-angle strokes (mixed radicals)forge_gap
draft on a quadric faceK2.2 tilt of a quadricforge_gap
boss + pilot (Solid ⊍ Cyl disjoint union)K2.3 mixed disjoint unionsforge_gap
curved-solid STEP export + STEP round-trip of a drilled plateK3.7 freeform STEPforge_gap
fillet on a non-planar baseK5.2 general blendsforge_gap
interference of face-touching / pocketed solidsK2.x boolean robustness on degenerate contactforge_gap

Deliberately dropped (not merely deferred), with an actionable message:

has no reader for that binary format. model_import and gitcad-convert

now return the STEP migration path (export STEP from FreeCAD, import that —

planar solids come in exactly). Restoring .FCStd would need either a native

OCCT-BREP reader or a FreeCAD-tree-only importer; neither is worth it pre-users.

Non-default features kept working on forge (no regression): model/section/

assembly 2D drawings, box/prism fillet & chamfer-all-edges, shell, planar STEP

import, mass properties + centroid, exact inertia (gitcad.analysis), feature

recognition of plate-with-holes, planar-part interference, the select-DSL

(area_max/length_max/axis extremes, now that entities() carries

centroid/area/length).

Migration (per the CLAUDE.md major-change process)

  1. 1. This ADR supersedes the relevant part of ADR-0018.
  2. 2. Build forge HLR behind the existing seam; the drawing engine switches to the

seam's projected-segment output.

  1. 3. Shadow-check: the new drawings render the corpus; visibility is verified

against the tessellation (a point is hidden iff a triangle occludes it).

  1. 4. Convert tests/** @pytest.mark.occt differentials to forge invariants or

delete; invariants/ + golden/ stay green with no kernel-marker.

  1. 5. Delete kernel/occt.py, kernel/auto.py, the occt extra, and the

cadquery-ocp dependency. Remove the occt pytest marker.