SmFace::ClassifyLoops still returned Unported for a target loop that crosses
no seam, although SmCurveClassification::GetLoopCrossings is ported. Call it
with the target loops, the face props and the bisector's classification, as
upstream does; its status stays ignored.
The query.face_p4 runner now also exports the face props the bisector and
GetLoopCrossings read (zone tolerance, no-vertex poles, pole points, missing
seam direction). Faces whose surface or loop edge curves the importer cannot
reproduce exactly report inexact_geometry on both sides when ClassifyLoops
may evaluate them, decided from the export as unit query.face_p3 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A seam call whose forked child crashes (or that the shim cannot fork for)
is an input undefined in C++ (UB-137), so face_p1's run_cpp now returns
Output::rejected naming UB-137 and the trap: the Rust side is not run and
nothing is compared. The generic runner no longer lets a C++-side deferral
win; a deferral from run_cpp is a harness error, so deferrals are decided
by the Rust side only. The runner test for the removed path now checks
both behaviours.
face_p1: 2315 cases, 2276 pass, 0 deferred, 37 rejected, 2 known
mismatches (was 37 deferred to cpp-crash-guard:UB-137).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At c9c5979 SmVector3d defaults to SM_UNDEF_DOUBLE, so the tangent a failed
GetEndTangent leaves in SmVertex::AreEdgesTangent / IsTopologicalVertex is
defined: AngleBetween reports 0 and both answer TRUE. The port returned
InvalidInput there and the topology.vertex shim refused the input. Read
Vec3::UNDEF instead, refuse only a null curve in the shim, and record WR-255
(correcting UB-106) plus the per-site #521 audit in findings.
70 compared cases in fixtures/topology.vertex/end_tangents.json (zero end
tangents, 1e-30 end spans) and a unit test on a composite-curve edge, whose
evaluation always fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow-up to #236 and #242: the three stubs on main that still named
blend.fillet_geom_p3 / blend.fillet_solver_p3.
- FilletCorner::calc_corner_vert_geom calls FilletBrep::calc_corner_vert_geom.
- FilletCorner::calc_corner_edge_geom calls the new
FilletBrep::calc_corner_edge_geom_of_corner, which resolves the edge's
corner to the calling corner (upstream asserts it is its own).
- ExecutiveAccess::solver_other_corner becomes a required adapter that
delegates to FilletSolver::other_fillet_corner, like solver_corner; the
runners and p3_tests refuse it as they refuse solver_corner.
The fillet_corner_p1 shim runs CalcCornerVertGeom / CalcCornerEdgeGeom on
corners with vertices / edges now (fixtures); generated corners keep empty
lists, since random models crash upstream's per-type calculators.
fillet_corner_p1: 173/174 passed, 0 failed, 1 deferred (was 170, 4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The FaceSweepSame fixture built its face natively with
SmBrep::CreateFaceFromSurface but in Rust as a bare face record, and a
failed native CreateFaceFromSurface was recorded as a rejected fixture.
The Rust runner now calls topology::brep::p8::create_face_from_surface on
a B-rep with its infinite region, and the shim returns the failed status
as the case output. The 11 cases that were rejected for it now compare
SM_ERR on both sides; gen0411 now defers on the face-owned surface cache
of a shell ray fire, so the unit is partial.
Also explains in findings.md the small fixture rejections in
blend.fillet_geom_p3, boolean.merge_p4 and offset.offset_executive_p2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Carries #613 onto main bcc2f80be. Conflicts:
- primitive/api_primitives.rs: keep main's comment on the torus point
classification; with #613's prepared trim cache the partial torus now
builds, so the test asserts a manifold solid instead of a deferral.
- plan/units notes (api_primitives, primitive_creation_p1, query.brep,
topology.brep_p10): keep main's CI status prefix and #613's update text.
query/face/p2.rs merged cleanly: main's WithMadeTrimCurves and mutable
face queries stay alongside #613's prepared-cache point classification.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings #596's topology.edge exact-surface transport up to date with main,
including the offset gen0730 importer fix. Merged without conflicts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expose initialized output on error without changing the existing Result API. Compare actual native output with real shifted-trim failure and success fixtures.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
- SurfaceIntersector::evaluate_law_point takes &mut FilletLaw, evaluates it and sets
the shared offset surfaces' distances (the stale dispatch-gap stubs are gone).
- DoLawIntersection / SplitSpan / ComputeLawPointValues are generic drivers over
IntersectorVirtuals, which gains EvaluateLawPoint, ComputePointValuesDelta and
TestSpanAccuracy; FilletIntersector::do_law_intersection dispatches them to the
fillet intersector's overrides. DoLawIntersection's guesses alias the end points'
own UVs, as upstream (LawGuess::PointUvs).
- SmVariableRadiusFS: SetEdgeArcLength wired, impl FilletSolverOps, and
ReCalcFilletGeom runs DoLawIntersection on its SmFilletIntersector.
- IntersectorSolver keeps base extensions in step between the solver's offsets and
the intersector's copies (OffsetSurface::follow_extension_of), as upstream's one
object does.
- Runners pass the shim's base SmFilletLaw; std_solver_p2's no-geometry recalc
cases keep the constructor's default fillet geometry on both sides instead of
being rejected.
surface_intersector_p2 1660/1660, surface_intersector_p3 1527/1527,
fillet_standard_solver_p2 405/405: all three green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runner compared the tracker's rm and add lists only, and its pool objects
were all bare SmTopology (type 16001), which the tracker's IsKindOf filter
ignores: the lists were always empty and capture's bookkeeping was unobservable.
Two-tier mutation testing found 11 mutants in `topology::topology` that neither
the unit tests nor this unit noticed.
- TRACK_LISTS now emits GetChgList and GetNotifyEvents (action, caller,
data1..3) on the C++ side and TrackTopologyChanges::lists() on the Rust side.
- A new SET_KIND op gives a pool object a stand-in topology type code
(SmTopology, SmFace, SmEdgeuse, SmEdge, SmVertex). The object keeps SmTopology
storage; SmTrackTopologyChanges classifies by type code alone, and the state
dump asks the base class explicitly, so its type answers are unchanged.
Existing cases keep the default kind.
- A fixed case drives every capture arm (ChangeGeometry, PostEdit, AddToBrep,
SplitInBrep, TrimNoSplitInBrep, MergeInBrep, RmFromBrep, Destruction) with a
tracker and dumps the lists and events after each, so later operations cannot
mask an arm.
difftest (Linux): PASS topology.topology cases=658 passed=658 failed=0
deferred=0 max_rel_err=0e0. Replaying the saved mutations: the ChangeGeometry/
PostEdit tracked selection, SplitInBrep, TrimNoSplitInBrep,
ChangeGeometry|Split|Merge, Destruction and remove_key mutants now fail the
unit. The owner-walk and accessor mutants remain unit-test coverage (they need
geometry stand-ins with owner links; #656 has the tests).
Co-Authored-By: Claude Code <noreply@anthropic.com>
The runner compared the tracker's rm and add lists only. Mutation testing found
11 mutants in `topology::topology` that neither the unit tests nor this runner
noticed; most change `capture`'s bookkeeping, which is only visible in the chg
list and the captured notify events (or through the owner map those events use).
TRACK_LISTS now emits `GetChgList` and `GetNotifyEvents` (action plus caller and
data1..3 indices) on the C++ side and the Rust tracker's `lists()` on the other,
so the gate compares the complete captured state.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Keep main shared face/axis defaults and consumed-curve slot semantics, while preserving production notification-aware deletion and native geometry comparisons.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
- edgeuse::p2::destroy (~SmEdgeuse), face::p3::destroy (~SmFace) and
shell::destroy (~SmShell): SM_NO_DESTRUCTION through the object's context
(status ignored, Unported propagates), the record dropped with what it owns,
then ~SmAObject's ClearAttributes. edge::with_object_context is shared with
edge::destroy.
- brep_p4: DeleteEdge's edge uses and merged-away face, DeleteVertex's vertex
shell. brep_p5: GlueVertices' shells. brep_p3 DeleteFace now deletes what
upstream deletes there: the easy branch's edge uses (it only unlinked them),
the other shell of a region merge, the emptied shell and the last new shell
of the nesting pass; destroy_face also clears the face's attributes.
- difftest: the brep_p3 recorders count SM_NO_DESTRUCTION per caller kind
(edge, edge use, face, shell); DeleteFace records end with the four counts.
All 90 delface cases agree (23 failed with only the brep_p4 changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TYEpC4tUKxEP3MCaFL5Kz2
Use live topology reads and cache-first face classification while retaining exact evaluation geometry. Forward constructor identities into stitching region queries and compare primitive geometry, tolerances, trims and rays against SMLib.
Co-Authored-By: Codex <noreply@openai.com>
Both GeomRecorders (brep_p3 shim and runner) count the SM_NO_DESTRUCTION
notifications whose caller is an edge (~SmEdge), and DeleteFace records end
with that count. 13 of the 90 delface cases destroy an edge through
DeleteEdge; without topology::edge::destroy they fail (C++ 1, Rust 0).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TYEpC4tUKxEP3MCaFL5Kz2
Sync with verified main 27b698e905 before the approved documentation-only clarification.
Co-Authored-By: OpenCode (GPT-6.1 Sol Fast) <noreply@openai.com>
Remove optional post-operation span reconstruction and its private snapshot dependency. Retain genuine delegating observers, tags 1 through 4 and both original 189-value per-side bit-identity gates. No corpus, exception, production, reference or tolerance changes.
Co-Authored-By: OpenCode GPT-6.1 Sol Fast <noreply@openai.com>
Forward only the public SmObject context allocator and matching ordinary/placement deletes, avoiding SmTopology's hidden allocator and SmObject's private overloads. No geometry, protocol, guard or fixture changes.
Co-Authored-By: OpenCode (GPT-6.1 Sol Fast) <noreply@openai.com>
Use the pinned getters' public UV matrix fields without casts or point copies. Make unit-local Rust probe helpers fallible and checked while retaining checkpoint payloads, successful evaluation order and the per-side original-record bit-identity gates.
Co-Authored-By: OpenCode GPT-6.1 Sol Fast <noreply@openai.com>
Add an opt-in real-face resolver and port the native UV/pole/seam degeneracy continuation. Compare real SmFace surfaces, independent mapped samples, and native IsDegenerate decisions in 16 new fixtures (27 calls). Preserve the original generator and opaque-owner paths; no tolerance or unit-status changes.
Co-Authored-By: OpenCode (GPT-6.1 Sol Fast) <noreply@openai.com>
Add six affine surface/cache cases and unit-local native grid observations while preserving the original 615 inputs and both opaque deferrals. Add canonical-CI identity, node-validation and mutation-sensitivity controls. No production paths or tolerances change.
Co-Authored-By: GPT-6.1 Sol Fast <noreply@openai.com>
Add unit-local native-feature checkpoints around the genuine plane solve, StepSolve return and ComputePointValues. Keep supplemental span reconstruction explicitly separate, and require instrumentation to preserve each original 189-value record bit-for-bit.
Co-Authored-By: OpenCode GPT-6.1 Sol Fast <noreply@openai.com>
sm_InsertSolutions now raises an edge-end vertex's tolerance to the edge's
(SmVertex::SetTolerance) and IntersectVertices runs the GWC:CHANGED_SSS block:
knot-snap cull, surface projections, sm_SetXSectCurveThroughPoint on the
classification curve, SetIsCurveCurrent(FALSE) and deleting the UV trim curves
of the vertex's edges and their topology intersector mates. The _mut entry
points take a ClassGeometryMut; the read-only ones defer where an edit is due.
New B-rep difftest cases (shim intersect_curve_class_p5_topo.cpp): 30 fixtures
and 300 generated, all bit-identical. Unit goes green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dBoSgwB37oA88rLG9LZ6E
PrimitiveAccess's CreatePlanarFace(s)With3DCurves, CreateFace(s)FromSurface
(topology.brep_p8) and DeleteFace (topology.brep_p3) defaults were UNPORTED
stubs; they now call the ported B-rep functions through
SweepAccess::with_brep_mut. PrimitiveModel's identical overrides are removed.
The difftest adapter gains a tag store, untagged curve lookups and face
deletion through KernelManifoldIntersector::delete_faces_with_keys, and every
entry-point record now also compares the B-rep's face/edge/vertex counts.
difftest: 296 cases, 295 bit-identical passes, 0 deferred (was 9), 1 UB-78
rejection, 0 failures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "SurfaceKind::Cone/Sphere (SmCone/SmSphere dispatch)" deferrals of
surface.surface_p2 were runner pre-checks keyed on the C++ face surface type,
not a missing Surface arm: Surface::read_from_db already returns Cone and
Sphere, and the shared exports carry such faces exactly (sc_analytic_export.h).
- blend.fillet_corner_p1/p2: defer on ImportTypes::inexact_surface (as p3).
- topology.face_p3: skip faces the import rebuilt exactly.
- topology.vertex: the shim emits the exact record head; the runner reads it.
topology.vertex: 5508 passed, 0 deferred (was 536 deferred) -> green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RvuXmr7S5RoUXxVohC1giE