You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the segmented duplication audit in #377. Passes 2a (#382), 2b (#383) and 3 (#384) are the prerequisites — check they are closed before starting.
This lane: meshing plus the Metal-visualization presentation and misc utilities.
Step 1 — Re-derive the scope. The list below is already stale.
The previous version said ~2,173 LOC. Measured 2026-08-19 it is 2,345; MeasurementHelpers.swift alone went 225 → 375.
This lane has the only confirmed detector hits of the four Pass 4 lanes. Measured 2026-08-19, detect-duplicate-logic.py --swift reports six candidate pairs repo-wide and two are yours:
pair
score
shared shingles
PresentationMesh.swift:45 / :120 — both shadedMesh
0.93
172
PresentationMesh.swift:85 / :158 — both edgeMesh
0.90
111
Two same-named overloads in one 185-line file, each near-duplicating its sibling. Start there. It is the highest-value known lead in any Pass 4 lane, and the other three lanes return nothing from this detector at all.
Files
This exact format is machine-read..claude/workflows/duplication-audit.js parses this section and expects one backtick-wrapped repo-relative path per line with a LOC count in parentheses. Keep the shape when you update the numbers, or the workflow aborts with "Could not resolve a file scope".
Sources/OCCTSwift/Mesh.swift (723)
Sources/OCCTSwift/MeasurementHelpers.swift (375)
Sources/OCCTSwift/PresentationMesh.swift (185)
Sources/OCCTSwift/ZLayerSettings.swift (184)
Sources/OCCTSwift/ClipPlane.swift (166)
Sources/OCCTSwift/Camera.swift (159)
Sources/OCCTSwift/KDTree.swift (110)
Sources/OCCTSwift/PixMap.swift (108)
Sources/OCCTSwift/Date.swift (106)
Sources/OCCTSwift/DisplayDrawer.swift (102)
Sources/OCCTSwift/OCCTSerialQueue.swift (64)
Sources/OCCTSwift/FontManager.swift (63)
The bridge side is Sources/OCCTBridge/src/OCCTBridge_Mesh.mm and OCCTBridge_Visualization.mm.
This method section is a deliberate copy. The canonical version lives in #377. It is
duplicated into every pass issue on purpose: a linked document does not get read, and this
programme has already lost work to instructions nobody followed. If you change the method, change #377 and the sibling passes too. This is the one place in this repo where duplication is correct.
If you are picking this up cold, start here
Written for someone with no prior context. Work through it in order.
What a "pass" is.#377 splits one repo-wide duplication sweep into thirteen per-layer passes. Each audits a fixed set of files for duplicated logic, files one sub-issue per confirmed finding, fixes them, and closes.
What "duplication" means here. Not just identical text. Four angles: a helper reimplemented under a second name, copy-pasted maths that has drifted, doc comments that no longer describe the code below them, and two parallel types that should be one. In this repo these are frequently live bugs. Pass 2a found Shape.fixed(...) silently discarding three parameters and two tolerance defaults drifted apart for the same question; Pass 3 found six call sites that had lost a null guard their siblings kept.
Behaviour fixes are in scope. If a duplication has a real defect on one side, fix it here. See #377's "Scope: behavior fixes are in scope".
Step 0 — Read the policies before you touch anything
Working rules live in okf/policies/. Read these five now:
Every issue needs type:* and priority:*. Pass 3 filed three issues with zero labels because the author wrote **Type:** chore in body prose instead of applying the label.
From origin/main, not local main (may be stale), not refactor/377-segmented-audit (dead since 2026-07-29).
One branch, not two. Pass 2a used a single branch merged straight to main. Pass 3 grew a second -integration branch with a PR between them, an extra layer nothing requires. If you do use one, issue-tracking.md says a merge into it is notDone on the board — cards stay at Code-Review until it reaches main.
--self-test every time. Three gate scripts here were confidently wrong because the detector had gone blind while still reporting "all clear" (#618, #624/#630, #626).
A clean detector run finishes Step 3, not the audit. It compares function bodies and is blind to angles 3 and 4 entirely. Pass 3's declared scope returned zero hits and the pass still produced five findings.
Step 4 — Run the audit workflow
.claude/workflows/duplication-audit.js, invoked with this issue number (388) as its argument. It reads the ## Files section, bin-packs it, and runs the four angles with an adversarial verify stage. Not the generic code-review workflow — that reads a diff and this pass has none.
It produces false positives. Nobody has measured this workflow's rate; the sibling over-coverage detector built in #928 measured 41% false over a 40-row hand-adjudicated sample. Expect the same order. Adjudicate every candidate against the real code.
Step 5 — Triage into sub-issues
One sub-issue per confirmed finding. Pass 2a produced 26, Pass 2b 13, Pass 3 five over a much smaller lane. .claude/workflows/duplication-triage.js drafts bodies from {parentIssue: 388, findings: [...]}; it deliberately does not create them, so each filing stays auditable.
A draft body must carry four sections. Pass 3's first three issues shipped with two and had to be backfilled:
Duplication site — every file:line with the symbol at each.
Divergence — the concrete behavioural or doc difference already observed, or an explicit statement that the copies still agree.
Tests — coverage found at each location by file and suite name, or an explicit "no coverage found" per location.
OCCT functions — the specific OCCT C++ class and method each side invokes, not just the C bridge function name.
Link each as a sub-issue — note -F, not -f; the field is numeric and -f fails with a confusing 422:
ID=$(gh api repos/SecondMouseAU/OCCTSwift/issues/<new-number> --jq '.id')
gh api -X POST repos/SecondMouseAU/OCCTSwift/issues/388/sub_issues -F sub_issue_id=$ID
Board: OCCTSwift Refactor (#377). Move cards at the moment the workflow visits them, not in a later sweep.
Step 6 — Fix
One PR per sub-issue, or one per tight cluster touching the same file.
Closes #<n> — repeat the keyword per issue.Closes #1, #2 closes only the first, and several issues here stayed open after their fix merged because the title said fix(#N) with no keyword.
## CHANGELOG entry in the body.
## SemVer impact in the body. Breaking? Say so there and do not touch docs/SEMVER.md.
Tests, each proven to fail against the unfixed code, and say so.
Gates before pushing — gate-scripts is the one required check on main:
gate-scripts is not the whole story. code-style is a separate required-in-practice job, and an
empty manifest makes it stricter, not weaker. See "Code style" below before you push anything.
Code style: the manifest being empty does not mean CI passes itself
New code has been failing the code-style CI job even with both manifests empty. That is not a
contradiction, it is what an empty manifest actually does: CI's swift-format lint --strict step
runs over "every file under Sources/OCCTSwift minus the manifest"
(.github/workflows/code-style.yml), and an empty manifest means that set is now the whole tree,
not just the files this PR touches. A latent violation anywhere in Sources/OCCTSwift can turn this
pass's PR red even if the diff never goes near it.
The pre-commit hook does not catch this either, by design: it runs the bridge clang-format check but
deliberately not swift-format or swiftlint (see the hook's own header comment, Scripts/git-hooks/pre-commit),
so a Swift-side style violation is currently push-and-find-out no matter how clean the manifest is.
Run CI's own commands yourself before pushing, so the first read of them isn't a red check:
swift-format format -i --configuration .swift-format <files you changed>
swift-format lint --strict --configuration .swift-format --recursive Sources/OCCTSwift
swiftlint lint --strict --config .swiftlint.yml
format -i fixes the mechanical stuff (indentation, spacing, blank lines) in place. lint --strict
is what actually gates, and reports things -i will not fix for you: fileScopedDeclarationPrivacy
(a file-scope declaration must be explicitly private) and orderedImports are the two that most
often surprise a first-time contributor here. Run lint over the whole tree as shown, not just your
changed files, since that's what CI does.
For the bridge side (only relevant if this pass touches a Sources/OCCTBridge file):
Scripts/format-bridge.sh # rewrites in place
Scripts/format-bridge.sh --check # what CI actually runs
Doc comments: stay to one sentence plus only the Parameter/Returns/Throws tags that add
something the summary doesn't; design rationale and examples belong in docs/, not in the comment. Scripts/comment-ratio-check.py reports (never fails) a file whose comment lines outnumber its code
lines, run it as a signal. A verbose doc comment is a findable style regression here, not a matter of
taste.
Step 7 — Close the pass
Close when every sub-issue is closed and you have answered the scope question in writing. Post a closing comment recording which files you claimed and which you handed on, anything found and deliberately not fixed with the reason, and any candidate that proved not real so the next pass does not re-investigate it. Pass 2a's closing comment is the model.
Traps this repo has actually hit
Where you put an extracted helper is a correctness decision, not a style one. Decide by reach: count every site with that logic, across every file, before choosing. A static helper in a .mm is confined to that translation unit, so a copy in another .mm can never converge on it and drifts independently.
This has shipped a defect twice in three days. occtComputeBoundingBox was file-static in OCCTBridge_Topology.mm, which is whyOCCTShapeGetBounds in OCCTBridge_Properties.mm was the one bounds entry point with no IsVoid() guard (fixed 2026-08-17, #943, by moving it to OCCTBridge_Internal.h as inline). Then Pass 3's own #949 made occtDocumentInit file-static in OCCTBridge_Document.mm while twelve copies sat in OCCTBridge_IO.mm, six already missing a null guard — that is #957.
Before extracting: grep -rn '<the distinctive call>' Sources/OCCTBridge/src/*.mm. More than one file → the helper goes in OCCTBridge_Internal.h as inline. Sites that are not identical are where the bug usually is; record the difference rather than flattening it.
The style manifest is a one-way ratchet. A file you touch must be brought fully clean and removed from Scripts/style-manifest-{swift,bridge}.txt in that same PR, and check-style-manifest.py refuses to let anything back on. Removing a file without actually making it clean just moves the failure to the linter, which is how main sat red for five merges (#942). OCCTBridge_Modeling.mm is the most format-sensitive file in the repo — check the manifest before touching it.
A green test suite is not coverage. Some tests are gated on OCCTSWIFT_LOCAL=1 and silently skip in CI, and the suite reports the same total either way — only the per-test line distinguishes them. If you rely on a test to prove something, confirm from the log that it started.
swift-format diagnostics strip inline code spans. A doc comment reading Split `self` by `tool`. is quoted back as "Split by .". The doubled spaces are an artefact of the message, not damage to the file (#942).
Building against the released kernel gives false results. Change anything under Sources/OCCTBridge/ and build from source. In a worktree, Libraries/ is its own tree — symlink it from the main checkout and set OCCTSWIFT_LOCAL=1.
Refetch before transcribing anything. Two branches adding a CHANGELOG entry both insert at the top of the same section; a stale base gives a conflict whose resolution silently drops the other side's work.
PresentationMesh, Camera, ClipPlane, ZLayerSettings, DisplayDrawer, PixMap, FontManager are the AIS/visualization surface. Much of it is thin wrapping where near-identical bodies are legitimate rather than duplication. Per scope-boundary.md, be careful that a "consolidation" here does not invent an abstraction OCCT does not have — this repo stays faithful to OCCT's own shape.
Ordering
Depends on: Pass 2a (#382), 2b (#383), 3 (#384). Parallel with: 4a (#385), 4b (#386), 4c (#387). Companion:#814 is this lane's refman-coverage audit and runs after this pass.
Part of the segmented duplication audit in #377. Passes 2a (#382), 2b (#383) and 3 (#384) are the prerequisites — check they are closed before starting.
This lane: meshing plus the Metal-visualization presentation and misc utilities.
Step 1 — Re-derive the scope. The list below is already stale.
The previous version said ~2,173 LOC. Measured 2026-08-19 it is 2,345;
MeasurementHelpers.swiftalone went 225 → 375.wc -l Sources/OCCTSwift/{Mesh,MeasurementHelpers,KDTree,PresentationMesh,ZLayerSettings,ClipPlane,Camera,DisplayDrawer,PixMap,FontManager,Date,OCCTSerialQueue}.swiftThis lane has the only confirmed detector hits of the four Pass 4 lanes. Measured 2026-08-19,
detect-duplicate-logic.py --swiftreports six candidate pairs repo-wide and two are yours:PresentationMesh.swift:45/:120— bothshadedMeshPresentationMesh.swift:85/:158— bothedgeMeshTwo same-named overloads in one 185-line file, each near-duplicating its sibling. Start there. It is the highest-value known lead in any Pass 4 lane, and the other three lanes return nothing from this detector at all.
Files
This exact format is machine-read.
.claude/workflows/duplication-audit.jsparses this section and expects one backtick-wrapped repo-relative path per line with a LOC count in parentheses. Keep the shape when you update the numbers, or the workflow aborts with "Could not resolve a file scope".Sources/OCCTSwift/Mesh.swift(723)Sources/OCCTSwift/MeasurementHelpers.swift(375)Sources/OCCTSwift/PresentationMesh.swift(185)Sources/OCCTSwift/ZLayerSettings.swift(184)Sources/OCCTSwift/ClipPlane.swift(166)Sources/OCCTSwift/Camera.swift(159)Sources/OCCTSwift/KDTree.swift(110)Sources/OCCTSwift/PixMap.swift(108)Sources/OCCTSwift/Date.swift(106)Sources/OCCTSwift/DisplayDrawer.swift(102)Sources/OCCTSwift/OCCTSerialQueue.swift(64)Sources/OCCTSwift/FontManager.swift(63)The bridge side is
Sources/OCCTBridge/src/OCCTBridge_Mesh.mmandOCCTBridge_Visualization.mm.If you are picking this up cold, start here
Written for someone with no prior context. Work through it in order.
What a "pass" is. #377 splits one repo-wide duplication sweep into thirteen per-layer passes. Each audits a fixed set of files for duplicated logic, files one sub-issue per confirmed finding, fixes them, and closes.
What "duplication" means here. Not just identical text. Four angles: a helper reimplemented under a second name, copy-pasted maths that has drifted, doc comments that no longer describe the code below them, and two parallel types that should be one. In this repo these are frequently live bugs. Pass 2a found
Shape.fixed(...)silently discarding three parameters and two tolerance defaults drifted apart for the same question; Pass 3 found six call sites that had lost a null guard their siblings kept.Behaviour fixes are in scope. If a duplication has a real defect on one side, fix it here. See #377's "Scope: behavior fixes are in scope".
Step 0 — Read the policies before you touch anything
Working rules live in
okf/policies/. Read these five now:measure-dont-assume.mdsearch-before-building.mdissue-tracking.mdtype:*andpriority:*. Pass 3 filed three issues with zero labels because the author wrote**Type:** chorein body prose instead of applying the label.changelog-on-merge.mdprove-the-test-fails.mdRead at the step that binds them:
semver-at-release.md(never editdocs/SEMVER.mdin a PR — missed twice, #829 and #943, both shipping an incomplete break table),code-style.md,docs-current.md,writing-style.md,code-structure.md.okf/index.mdlists the rest.CLAUDE.mdat the repo root has a long "Known OCCT Bugs" section — read the entries for this lane.Step 2 — Branch
From
origin/main, not localmain(may be stale), notrefactor/377-segmented-audit(dead since 2026-07-29).One branch, not two. Pass 2a used a single branch merged straight to
main. Pass 3 grew a second-integrationbranch with a PR between them, an extra layer nothing requires. If you do use one,issue-tracking.mdsays a merge into it is notDoneon the board — cards stay atCode-Reviewuntil it reachesmain.Step 3 — Run the duplication detector
python3 Scripts/repro/784-duplication-rescan/detect-duplicate-logic.py --self-test # always first python3 Scripts/repro/784-duplication-rescan/detect-duplicate-logic.py --swift python3 Scripts/repro/784-duplication-rescan/detect-duplicate-logic.py --bridge--self-testevery time. Three gate scripts here were confidently wrong because the detector had gone blind while still reporting "all clear" (#618, #624/#630, #626).A clean detector run finishes Step 3, not the audit. It compares function bodies and is blind to angles 3 and 4 entirely. Pass 3's declared scope returned zero hits and the pass still produced five findings.
Step 4 — Run the audit workflow
.claude/workflows/duplication-audit.js, invoked with this issue number (388) as its argument. It reads the## Filessection, bin-packs it, and runs the four angles with an adversarial verify stage. Not the generic code-review workflow — that reads a diff and this pass has none.It produces false positives. Nobody has measured this workflow's rate; the sibling over-coverage detector built in #928 measured 41% false over a 40-row hand-adjudicated sample. Expect the same order. Adjudicate every candidate against the real code.
Step 5 — Triage into sub-issues
One sub-issue per confirmed finding. Pass 2a produced 26, Pass 2b 13, Pass 3 five over a much smaller lane.
.claude/workflows/duplication-triage.jsdrafts bodies from{parentIssue: 388, findings: [...]}; it deliberately does not create them, so each filing stays auditable.A draft body must carry four sections. Pass 3's first three issues shipped with two and had to be backfilled:
file:linewith the symbol at each.Creating and labelling
[Pass 4d]in the title. Thephase:4dlabel carries that. ConstructionPlane.tangentToFace ignores theatvertex when computing the plane normal #879/ConstructionContext / ConstructionLayer: plane/axis/point storage, accessors, and materialization are hand-triplicated with no shared entity abstraction #886/Surface.torusAxis / Surface.revolutionAxis duplicate the same six-var bridge-unwrap body in ShapeAxis.swift #891 from Pass 2b are the convention; Pass 3 used a prefix and it had to be stripped.**Type:** chorein the body is not a label, and CI flags the issue as unlabelled.type:bugvstype:choreis decided by checking, not assuming. Only call itchoreonce you have confirmed the copies still agree. Pass 3's occtDocumentInit is file-static, so 12 duplicate sites in OCCTBridge_IO.mm cannot reach it (and 6 have already lost the null guard) #957 began as drift-prevention and became atype:bugwhen six of twelve sites turned out to have already lost a null guard.priority:P2if a caller can observe the divergence today,P3if the copies agree,P1only for a crash or data loss.Link each as a sub-issue — note
-F, not-f; the field is numeric and-ffails with a confusing 422:Board: OCCTSwift Refactor (#377). Move cards at the moment the workflow visits them, not in a later sweep.
Step 6 — Fix
One PR per sub-issue, or one per tight cluster touching the same file.
Closes #<n>— repeat the keyword per issue.Closes #1, #2closes only the first, and several issues here stayed open after their fix merged because the title saidfix(#N)with no keyword.## CHANGELOG entryin the body.## SemVer impactin the body. Breaking? Say so there and do not touchdocs/SEMVER.md.Gates before pushing —
gate-scriptsis the one required check onmain:gate-scriptsis not the whole story.code-styleis a separate required-in-practice job, and anempty manifest makes it stricter, not weaker. See "Code style" below before you push anything.
Code style: the manifest being empty does not mean CI passes itself
New code has been failing the
code-styleCI job even with both manifests empty. That is not acontradiction, it is what an empty manifest actually does: CI's
swift-format lint --strictstepruns over "every file under
Sources/OCCTSwiftminus the manifest"(
.github/workflows/code-style.yml), and an empty manifest means that set is now the whole tree,not just the files this PR touches. A latent violation anywhere in
Sources/OCCTSwiftcan turn thispass's PR red even if the diff never goes near it.
The pre-commit hook does not catch this either, by design: it runs the bridge clang-format check but
deliberately not
swift-formatorswiftlint(see the hook's own header comment,Scripts/git-hooks/pre-commit),so a Swift-side style violation is currently push-and-find-out no matter how clean the manifest is.
Run CI's own commands yourself before pushing, so the first read of them isn't a red check:
format -ifixes the mechanical stuff (indentation, spacing, blank lines) in place.lint --strictis what actually gates, and reports things
-iwill not fix for you:fileScopedDeclarationPrivacy(a file-scope declaration must be explicitly
private) andorderedImportsare the two that mostoften surprise a first-time contributor here. Run
lintover the whole tree as shown, not just yourchanged files, since that's what CI does.
For the bridge side (only relevant if this pass touches a
Sources/OCCTBridgefile):Doc comments: stay to one sentence plus only the
Parameter/Returns/Throwstags that addsomething the summary doesn't; design rationale and examples belong in
docs/, not in the comment.Scripts/comment-ratio-check.pyreports (never fails) a file whose comment lines outnumber its codelines, run it as a signal. A verbose doc comment is a findable style regression here, not a matter of
taste.
Step 7 — Close the pass
Close when every sub-issue is closed and you have answered the scope question in writing. Post a closing comment recording which files you claimed and which you handed on, anything found and deliberately not fixed with the reason, and any candidate that proved not real so the next pass does not re-investigate it. Pass 2a's closing comment is the model.
Traps this repo has actually hit
Where you put an extracted helper is a correctness decision, not a style one. Decide by reach: count every site with that logic, across every file, before choosing. A
statichelper in a.mmis confined to that translation unit, so a copy in another.mmcan never converge on it and drifts independently.This has shipped a defect twice in three days.
occtComputeBoundingBoxwas file-static inOCCTBridge_Topology.mm, which is whyOCCTShapeGetBoundsinOCCTBridge_Properties.mmwas the one bounds entry point with noIsVoid()guard (fixed 2026-08-17, #943, by moving it toOCCTBridge_Internal.hasinline). Then Pass 3's own #949 madeocctDocumentInitfile-static inOCCTBridge_Document.mmwhile twelve copies sat inOCCTBridge_IO.mm, six already missing a null guard — that is #957.Before extracting:
grep -rn '<the distinctive call>' Sources/OCCTBridge/src/*.mm. More than one file → the helper goes inOCCTBridge_Internal.hasinline. Sites that are not identical are where the bug usually is; record the difference rather than flattening it.The style manifest is a one-way ratchet. A file you touch must be brought fully clean and removed from
Scripts/style-manifest-{swift,bridge}.txtin that same PR, andcheck-style-manifest.pyrefuses to let anything back on. Removing a file without actually making it clean just moves the failure to the linter, which is howmainsat red for five merges (#942).OCCTBridge_Modeling.mmis the most format-sensitive file in the repo — check the manifest before touching it.A green test suite is not coverage. Some tests are gated on
OCCTSWIFT_LOCAL=1and silently skip in CI, and the suite reports the same total either way — only the per-test line distinguishes them. If you rely on a test to prove something, confirm from the log that it started.swift-formatdiagnostics strip inline code spans. A doc comment readingSplit `self` by `tool`.is quoted back as"Split by .". The doubled spaces are an artefact of the message, not damage to the file (#942).Building against the released kernel gives false results. Change anything under
Sources/OCCTBridge/and build from source. In a worktree,Libraries/is its own tree — symlink it from the main checkout and setOCCTSWIFT_LOCAL=1.Refetch before transcribing anything. Two branches adding a CHANGELOG entry both insert at the top of the same section; a stale base gives a conflict whose resolution silently drops the other side's work.
What this lane already knows
MeasurementHelpers.swiftwas already deduplicated once, by Pass 2b. Edge fraction→parameter clamp+lerp duplicated 3x across ConstructionEntity.swift and MeasurementHelpers.swift with no shared helper #888 and Face UV-midpoint representative-point/normal sample duplicated across ConstructionEntity.swift and MeasurementHelpers.swift (7 sites) #889 pulled the edge fraction-to-parameter clamp+lerp (3 copies) and the face UV-midpoint sample (7 sites) out of it andConstructionEntity.swift. It has since grown 225 → 375 lines. Check whether the shared helpers are still being used, or whether new call sites re-inlined the same logic — a file that regained 150 lines after a dedup is worth reading closely.OCCTSerialQueue.swiftis concurrency-critical.Scripts/tsan-stress.shis required for any change touching it — see CLAUDE.md's Build & Test Commands anddocs/thread-safety.md. Do not treat a lock as duplication and remove it: CLAUDE.md's Known OCCT Bugs records a long list of kernel races (SheetMetal.Builder.build silently returns an empty solid under parallel test execution (8/10 runs) — contradicts the documented "independent shapes are thread-safe" guarantee #298, NCollection race under parallel execution: documented downstream as 'run --no-parallel', never filed or characterised upstream — needs the #298 TSan protocol #341, Uncatchable SIGSEGV in parallel swift test run, right after concurrent OBJ imports — possibly related to #341, unconfirmed #344, Concurrent Save/SaveAs to the same format corrupts a shared, cached storage driver instance (SIGSEGV) #349, CDM_Application::myMetaDataLookUpTable unsynchronized (race surfaced by #349 fix) #353, Stop using XCAFApp_Application::GetApplication() singleton — per-instance TDocStd_Application instead #371, Resource_Manager::Debug and Storage_Schema::ICurrentData() races surfaced by #371 (upstream OCCT#1398) #374) whose bridge-side mitigations look redundant and are not.ocafStoreMutex()in particular was kept after a refactor made it appear unnecessary, because it turned out to also serialize an unrelated hazard.Mesh.swiftand the mesh bridge carry known kernel history.OCCTMeshUnion/Subtract/Intersectwere deduplicated in PR fix(#794): factor shared scaffolding for 4 sibling-entry-point pairs #935 (Census: 11 bridge sibling-entry-point pairs share setup/extraction scaffolding but not implementation #794). CLAUDE.md's Known OCCT Bugs coversXCAFDoc_ShapeTool::theAutoNaming(NCollection race under parallel execution: documented downstream as 'run --no-parallel', never filed or characterised upstream — needs the #298 TSan protocol #341), reachable through OBJ and glTF import, and Shape.mesh() normalizes tessellation orientation for a valid solid; Shape.loadSTL() doesn't reliably preserve a globally-reversed STL's winding either #375's not-a-bug verdict on STL winding.PresentationMesh,Camera,ClipPlane,ZLayerSettings,DisplayDrawer,PixMap,FontManagerare the AIS/visualization surface. Much of it is thin wrapping where near-identical bodies are legitimate rather than duplication. Perscope-boundary.md, be careful that a "consolidation" here does not invent an abstraction OCCT does not have — this repo stays faithful to OCCT's own shape.Ordering
Depends on: Pass 2a (#382), 2b (#383), 3 (#384). Parallel with: 4a (#385), 4b (#386), 4c (#387). Companion: #814 is this lane's refman-coverage audit and runs after this pass.