v1.3.0 — find_correspondences (closes #24) + history-fix
OCCTMCP v1.3.0 — find_correspondences (closes #24) + history-fix
Adds the cross-body selection-mapping tool that closes the last gap in the remap matrix, and fixes two latent bugs that turned out to hide each other.
find_correspondences — new tool (50 total)
Maps selectionIds from a source body onto a target body that's a known transform of the source — typically a mirror_or_pattern output. Different contract from remap_selection: not "X mutated into Y on the same body" but "X on body A corresponds to Y on body B under transform T".
find_correspondences({
sourceSelectionIds: ["sel:src#face[3]"],
targetBodyId: "mirror-src",
transform: {
kind: "mirror",
planeOrigin: [0, 0, 0],
planeNormal: [1, 0, 0]
}
}) → { correspondences: [{ sourceSelectionId, targetSelectionId, fate, confidenceMm }] }
v1 supports translate / mirror / rotate hints. Compound transforms, bbox-alignment inference, and reading mirror_or_pattern manifest metadata as transform defaults are tracked as follow-ups in #24.
Fixed: identity history was a placebo
OCCT's TopologyGraph.findDerived returns empty for identity records (original == replacement). The recordIdentityHistory loop in HistoryRegistry was writing exactly such no-op records — so findDerived always returned empty and remap_selection fell back to the centroid heuristic. Every prior test asserting fate=preserved + confidenceMm=0 was passing because the centroid heuristic also returned 0 (non-overlapping unions, holes on opposite-side faces, etc.).
HistoryRegistry now stores an Entry { graph, isIdentityPreserving }. Identity-preserving recorders set the flag and skip the self-record loop. RemapTools treats findDerived empty + flag set as preserved-at-same-index. transform_body's history path is real for the first time.
Fixed: JSON ints failing in tool dispatch
When a client encodes Value.double(0), the SDK's JSONEncoder writes 0 (no decimal). The decoder picks .int(0). Tool dispatches reading coordinate arrays via arr[N].doubleValue got nil. LLMs sending [0, 0, 1] for unit vectors hit this constantly.
New Value.asDouble extension accepts either .int or .double; 11 array sites + 3 scalar sites converted across transform_body / mirror_or_pattern / select_topology / find_correspondences etc.
Updated history opt-in matrix
| Tool | Path | Notes |
|---|---|---|
transform_body |
identity flag | every node maps 1:1; flag short-circuits findDerived empty to preserved |
heal_shape |
identity flag if guards pass | falls back to heuristic if topology counts changed |
boolean_op |
per-input via *WithFullHistory |
recorded under output + both inputs |
apply_feature |
per-feature via BuildResult.histories[id] |
every spec kind covered (OCCTSwift v1.0.4) |
mirror_or_pattern |
use find_correspondences instead |
different contract — pattern instances aren't OCCT-derivatives |
Tests
25 swift-testing cases (was 24). New findCorrespondencesAcrossMirror builds a 20³ box at +X, mirrors about YZ, picks a face on the source, calls find_correspondences, asserts fate=matched + targetSelectionId.hasPrefix("sel:mirror-src#face[") + confidenceMm < 0.01. The existing historyRemapPreservesAcrossTransform now actually exercises the history path (was a placebo before this release).