Skip to content

investigate(#597): the two remaining bridge-side sites cannot be fixed by rejecting on error - #751

Merged
gsdali merged 5 commits into
refactor/381-pass1bfrom
fix/597-bridge-modeling-healing-approx-error
Aug 7, 2026
Merged

investigate(#597): the two remaining bridge-side sites cannot be fixed by rejecting on error#751
gsdali merged 5 commits into
refactor/381-pass1bfrom
fix/597-bridge-modeling-healing-approx-error

Conversation

@gsdali

@gsdali gsdali commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

What & why

#597's own comment (verified by #741) narrowed the remaining scope to bridge-side sites in
OCCTBridge_Modeling.mm/OCCTBridge_Healing.mm that run an approximation and never read the error
it reports. Census (per docs/v2.0.0-plan.md's census-once rule) found two candidates:
occtPlateApproxSurface (shared helper backing OCCTShapePlatePoints/OCCTShapePlateCurves in
OCCTBridge_Healing.mm, plus four more entry points in OCCTBridge_ProjLib_NLPlate.mm) and
OCCTShapeFillBuildResult (backing OCCTShapeFill/OCCTShapeFillWithSupport/
OCCTShapeFillConstraints in OCCTBridge_Healing.mm), plus several sites that turned out not to
need anything: ShapeUpgrade_UnifySameDomain's two bridge sites confirm #741's own finding (the
OCCT class exposes no error API at all); PipeShellBuilder/FillingSurface already expose their
underlying class's error as an opt-in accessor (the deliberate manual-builder design, not an
oversight); ShapeCustom_BSplineRestriction self-polices in the kernel (declines a face's
conversion rather than ever accepting one out of tolerance).

Both real candidates were built and measured against the actual bridge, not just reasoned about.
Both regress real, already-shipped, already-tested behaviour, for two different reasons:

  • GeomPlate_MakeApprox::ApproxError() measures fidelity to an intermediate object
    (GeomPlate_Surface) the caller never sees, not fidelity to the caller's own input points/curves,
    which is what the six entry points' actual contract (and their existing tests) check. On the
    GeomPlate_MakeApprox drives the #522 approximator directly, and no census counts it #571 fixture it exceeds tolerance by up to 5.8x while the deviation from the caller's own input
    points stays inside it throughout. Gating on it broke all 6 Issue571PlateApproxTests.
  • BRepOffsetAPI_MakeFilling::G0Error() is the right kind of number (distance to the caller's own
    constraints), but the bridge's effective Tol3d is always 1e-4, FillingParameters's own
    Swift default, not a fallback for an unset value, and legitimate, correct, higher-continuity or
    heavily-constrained fills routinely exceed it. Gating on it broke 2/17 FillingSupportFaceTests
    whose surfaces are provably correct by their own geometric assertions.

Both changes were reverted. What ships is the measurement: two doc comments recording exactly why
(so the same fix is not re-attempted and re-reverted), a durable CLAUDE.md Known OCCT Bugs entry
recording the same investigation and conclusion (see "Review response" below), and the full
investigation with five standalone reproducers under
Scripts/repro/597-bridge-modeling-healing-approx-error/.

No functional change. This does not close #597. It fully accounts for the bridge-side scope
the issue's own comment left open, and the conclusion is that none of it is safely fixable the way
#741 fixed OCCTGeomFillSweep. The remaining, deliberately out-of-scope kernel defect
(GeomFill_Sweep.cxx:325, where SError is overwritten with the requested tolerance instead of the
measured one inside the myForceApproxC1 branch) is unchanged; it needs a carried patch and its own
reproducer, tracked on #597 and #726, not a bridge-side check.

Refs #597, #668, #726, #741

Review response

Two reviews landed on the original commit: one automated pass (4 findings, all in the investigation's
supporting files) and a follow-up pass (a 5th, more consequential finding, added as an inline
comment). All five are addressed here.

  1. Hand-typed census table conflicts with the census-once rule. Valid. Cluster E (Cluster E: approximations accepted without reading the error they report #668) already
    has an executable census artifact at
    Scripts/repro/572-approx-consumer-sweep/,
    which is GeomFill_Sweep.cxx overwrites a measured approximation error with the requested tolerance #597's own origin. Scripts/repro/597-bridge-modeling-healing-approx-error/README.md's
    ## Census section now points there as the entry point instead of restating its own table, and
    the wider family GeomFill_Sweep.cxx overwrites a measured approximation error with the requested tolerance #597 established (every approximation-error-reporting class in
    OCCTBridge_Modeling.mm/OCCTBridge_Healing.mm, not just GeomConvert_ApproxSurface consumers)
    has been added to the 572 artifact's own README (its "GeomFill_Sweep.cxx overwrites a measured approximation error with the requested tolerance #597: the wider family" section) rather
    than duplicated here.

  2. Dead multi-span knot vector in wavyEdge(), plus a comment mislabeling a degree-13 curve as
    "cubic".
    Moot: wavyEdge() was rewritten wholesale to fix finding 5 below, and the rewrite has
    no dead code and no stale comment. Nothing left to fix separately.

  3. The "gating on ApproxError()/G0Error() regresses shipped behaviour" conclusion lived only
    as inline comments.
    Added a CLAUDE.md ## Known OCCT Bugs entry (matching the length and
    detail of its neighbours) recording the investigation, both candidate fixes, why each was
    rejected, and the corrected finding-5 measurement below. The two inline comments
    (OCCTBridge_Internal.h on occtPlateApproxSurface, OCCTBridge_Healing.mm on
    OCCTShapeFillBuildResult) are unchanged; the new entry is the durable, grep-from-one-place
    record that survives the pending file breakdowns (Break down Document.swift (17,603 LOC): only 14% of it is Document, evict the rest first #393-Break down OCCTBridge.h (21,896 LOC) into 16 per-domain headers: the split is fully derived, 4021 symbols, 0 ambiguous #395).

  4. Dead nKnots variable in occt_597_bsplinerestriction_selfpolice.mm, off by one from what is
    actually used.
    Removed. Rebuilt and re-ran; output is byte-identical to before the removal.

  5. The fixture does not build what it claims, the important one. wavyEdge() in
    occt_597_fill_g0_realistic.mm built its sine-sampled points as poles of a single-span,
    full-multiplicity Geom_BSplineCurve, i.e. a degree-13 Bezier, not an interpolant. Independently
    verified the review's own Bernstein-basis measurement before touching anything: for this
    function's exact parameters (amp=4.0, waves=3, npts=14), control-point peak 3.971, realized
    curve peak 1.4895 (rounds to 1.490), so 62.8% of the intended amplitude is gone, matching the
    review comment exactly. Fixed by rebuilding wavyEdge() on GeomAPI_Interpolate, which actually
    interpolates the sampled points, matching this repo's own naming precedent
    (OCCTCurve3DInterpolate in OCCTBridge_Curve3D.mm is GeomAPI_Interpolate-based;
    GeomAPI_PointsToBSpline, this repo's "FitPoints", approximates rather than interpolates and
    would have the same problem here).

    Re-measured, and the numbers moved dramatically:

    tol3d=0.0001     isDone=true  G0Error=0.20369      bboxDiag=1044.12    EXCEEDS TOLERANCE, ACCEPTED ANYWAY
    tol3d=1e-05      isDone=true  G0Error=0.203757     bboxDiag=1043.96    EXCEEDS TOLERANCE, ACCEPTED ANYWAY
    tol3d=1e-06      isDone=true  G0Error=0.226344     bboxDiag=1247.59    EXCEEDS TOLERANCE, ACCEPTED ANYWAY
    

    versus the old (damped) fixture's G0Error=5.89516e-05, within tol at the bridge's own default.
    The corrected boundary exceeds even the loosest tested tolerance, the bridge's own 1e-4
    default, by roughly 2000x. Added a bounding-box and control-pole diagnostic to the script while
    investigating why the error looked so large: the accepted surface's poles land up to ~530 units
    from a boundary whose own extent is about 10 units across. IsDone() is still true, and
    G0Error() alone does not communicate how far the fit has actually diverged.

    Does the PR's conclusion survive? Yes, on the part that was measured. A follow-on sentence
    that was not measured has been corrected, narrower, rather than left standing.
    The load-bearing
    evidence for "gating on the caller's own requested tolerance regresses shipped behaviour" was
    always the confirmed regression against FillingSupportFaceTests (2/17 failures on correct,
    already-tested surfaces), independent of this fixture, and that conclusion is unchanged.

    A second sentence went further, claiming no single G0Error() threshold at all could separate a
    genuinely diverged fit from the two regressing tests, without ever measuring those two tests' own
    G0Error() values. A second coordinator review caught this as an unmeasured, load-bearing claim.
    Measured it: instrumented OCCTShapeFillBuildResult/OCCTFillingBuild with a temporary debug
    print of G0Error(), ran each of the 17 FillingSupportFaceTests individually so each value maps
    to one test unambiguously, reverted afterward (no production code changed). The two regressing
    values are 5.295e-4 (curvatureContinuityIsAccepted's .g2 branch, 5.3x the 1e-4 tolerance
    every test uses) and 1.231e-3 (internalConstraintIsNotABoundary's interior-pull branch, 12.3x
    tolerance), both two to three orders of magnitude below the corrected fixture's ~2000x-2260x. A
    fixed absolute threshold placed between them (e.g. 0.01) would in fact separate today's known
    cases. The honest conclusion is narrower than "no threshold can work": gating on the caller's
    requested tolerance is unsafe and proven so; a fixed threshold is unjustified rather than
    impossible, since nobody has measured what it should be, which is exactly the failure mode Hunt for values that were never computed but are returned as measurements (hardcoded constants, pinned fixture counts, name-prefix censuses) #726
    exists to catch. Full detail, including the per-test measurement table, is in
    Scripts/repro/597-bridge-modeling-healing-approx-error/README.md.

CHANGELOG entry

None, investigation only, no functional change (#597)

Measured whether the two remaining bridge-side approximation sites in OCCTBridge_Modeling.mm/
OCCTBridge_Healing.mm could be fixed the way #741 fixed OCCTGeomFillSweep (reject a result whose
reported error exceeds the requested tolerance). Both regressed real, already-shipped, already-
tested behaviour when actually built and tested, for two different reasons (see the PR description
and Scripts/repro/597-bridge-modeling-healing-approx-error/README.md), so neither change shipped.
Two comments were added recording the measurement, plus a CLAUDE.md Known OCCT Bugs entry making
the same record durable across the pending file breakdowns. A PR review found that one of the
investigation's own supporting fixtures understated its boundary's difficulty; corrected and
re-measured, the underlying conclusion holds and is now backed by a harder, more convincing case.

SemVer impact

NONE. No production code behaviour changed; only comments, a CLAUDE.md entry, and
Scripts/repro/ content were added or corrected.

Checklist

  • New or changed behavior is covered by a unit test in the same PR: N/A, no behavior changed.
    The investigation's evidence is the existing test suites (Issue571PlateApproxTests,
    FillingSupportFaceTests) failing against the candidate fix and passing again once reverted;
    see "Notes for the reviewer".
  • Every new test and every new --self-test case was run once with its subject broken: N/A,
    no new test was added (see above); the policy's spirit is satisfied by running the existing
    suites both with the candidate fix present (fails, 6/6 and 2/17) and reverted (passes, full
    suite green).
  • The CHANGELOG entry above is complete, and docs/CHANGELOG.md is not in this diff.
  • The SemVer impact above is stated, and docs/SEMVER.md is not in this diff.

Notes for the reviewer

Why this PR has no Closes line but also isn't purely a no-op. #597, as re-scoped by its own
comment, asked specifically for the bridge-side sites in these two files. That scope is now fully
investigated: the census is complete, and every candidate is accounted for (fixed by #741, not
reachable, already correct by design, self-policing in the kernel, or unsafe to fix the obvious way,
measured). There is no remaining bridge-side work in OCCTBridge_Modeling.mm/OCCTBridge_Healing.mm
to do for this issue. Recommend #597 stays open only for the kernel-side GeomFill_Sweep.cxx:325
fix already noted in its own comment thread, which this PR does not touch.

Both regressions were confirmed against the real bridge, not inferred. For each candidate: (1)
implement the reject-on-error-exceeds-tolerance check, (2) swift build from source
(env -u OCCTSWIFT_LOCAL), (3) run the relevant existing suite and observe it fail, (4) revert, (5)
re-run and observe it pass again. This is the same "prove the test fails" discipline applied in
reverse: proving a proposed fix fails before ruling it out, not just a new test.

All 5 gate scripts and their --self-tests green: check-bridge-index, check-null-handle-guards,
check-docs-defaults, derive-bridge-header-split --verify, count-operations; the census and
merge-history audit self-tests also pass. Full suite (swift test): all tests pass, no failures
introduced by this review response (no production code changed).

…d by rejecting on error

Cluster E (#668). #597's own comment (verified by #741) narrowed remaining scope to bridge-side
sites in OCCTBridge_Modeling.mm/OCCTBridge_Healing.mm. Census found two candidates that run an
approximation and never read the error it reports (occtPlateApproxSurface, OCCTShapeFillBuildResult)
plus several that are not fixable or already correct (ShapeUpgrade_UnifySameDomain has no error API;
PipeShellBuilder/FillingSurface already expose theirs; ShapeCustom_BSplineRestriction self-polices).

Building and testing the obvious fix (reject when the error exceeds tolerance, #741's pattern for
OCCTGeomFillSweep) against the real bridge regressed real, already-shipped, already-tested behaviour
in both cases: 6/6 Issue571PlateApproxTests and 2/17 FillingSupportFaceTests failures. Measured why
and reverted; see Scripts/repro/597-bridge-modeling-healing-approx-error/README.md. Two doc comments
record the measurement so the fix is not re-attempted and re-reverted.

No functional change. Full suite: 5442 tests, 0 failures. Gate scripts: all 5 green.

@secondmouseAU-bot secondmouseAU-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(/code-review --comment — run via the actual code-review skill. Note: despite --comment being passed, nothing was posted by the skill itself; posted manually after independent verification. No correctness bugs found in the two production files touched — both hunks are pure comment insertions above unmodified function bodies, confirmed by three independent finder angles, matching the PR's own "no functional change" claim.)


Code review

Found 4 issues, all in the investigation's supporting files rather than the (unchanged) production code:

  1. The PR's ## Census section is a hand-typed markdown table, which conflicts with docs/v2.0.0-plan.md's census-once rule ("A census artifact is a probe under Scripts/repro/<cluster>/ that enumerates and measures its family against the real kernel... Not a grep, not a list in an issue body... Every issue in the cluster consumes that artifact instead of re-deriving it") for a family (Cluster E, #668) that already has exactly this kind of executable census at Scripts/repro/572-approx-consumer-sweep/, which already identified GeomFill_Sweep/ShapeUpgrade_UnifySameDomain as the defect this PR investigates and says "filed as its own issue" — i.e. is #597's own origin. A future contributor auditing this cluster has no single entry point without already knowing both artifacts exist.

    left as remaining scope: **the bridge-side sites in `OCCTBridge_Modeling.mm` and
    `OCCTBridge_Healing.mm`.**
    ## Census
    Every OCCT class in the two files capable of reporting a fitting/approximation error, and what
    happens to that error:

  2. wavyEdge() builds a full multi-span clamped B-spline knot vector that's never used, then falls through to a different single-span construction — and the trailing comment mislabels the actual result as a "Bezier-like single-span cubic" when the real curve degree is npts - 1 (13 for npts=14), not cubic. A future reader tuning the repro's boundary "hardness" via the dead degree = 3 variable would be editing a value with no effect, and would misjudge the curve's real degree from the stale comment.

    // ideal). Instead, hand-build a clamped cubic-ish BSpline by just using the points as poles
    // over a uniform non-rational BSpline (visually wavy, not exactly interpolating, which is fine
    // for a "hard to approximate" boundary probe).
    int nPoles = npts;
    int degree = 3;
    int nKnots = nPoles - degree + 1 > 1 ? nPoles - degree + 1 : 2;
    TColStd_Array1OfReal knots(1, nKnots);

  3. The record of "we investigated gating on ApproxError()/G0Error() and it regresses shipped behavior" lives only as inline doc comments in two functions, with no entry in this repo's own CLAUDE.md ## Known OCCT Bugs log — the established durable, grep-from-one-place record for exactly this investigated-and-rejected pattern. A future refactor that touches or deletes these two comments (e.g. the pending file breakdowns #393#395) carries no compensating signal forward, risking the same rejected fix being silently re-attempted.

    // order-1 answer is GeomAbs_G1.
    //
    // Returns a null handle when the approximation cannot be built; callers treat that as failure.
    //
    // #597 investigated whether ApproxError() should also gate acceptance here (GeomPlate_MakeApprox's
    // own doc promises Tol3d only "if possible"). Measured and reverted: ApproxError() is "the
    // distance between the entire target BSpline surface and the entire original [GeomPlate_Surface]",

  4. int nKnots = nu - degree; is computed and never referenced again — the real knot-array sizes on the next line use nu - degree + 1/nv - degree + 1 directly. Not a functional bug (the real construction is internally consistent), but the dead variable's value is off-by-one from what's actually used, which could mislead a reader verifying the knot-vector shape from this evidence file.

    // A wavy, high-degree (7), many-knot (10 spans) BSpline surface -- forces the general
    // approximation branch (ConvertSurface's "if (aSurf->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))"
    // branch at line 742 falls through because UDeg/VDeg (7) and NbSeg (100) exceed tiny caps).
    int nu = 17, nv = 17; // 17 poles, degree 7 => 10 interior knots => many spans
    TColgp_Array2OfPnt poles(1, nu, 1, nv);
    for (int i = 1; i <= nu; i++)
    for (int j = 1; j <= nv; j++)

🤖 Generated with Claude Code — posted manually; the skill's --comment flag did not post this itself.

@secondmouseAU-bot secondmouseAU-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up from a second /code-review --comment run (its own posting silently failed again — same tool bug as the review above and as PR #758 earlier today). Re-checked its 2 findings against the existing review: the dead-nKnots-variable one duplicates finding #4 above, skipped. The wavyEdge() amplitude-damping one is genuinely new and independently verified — see inline comment.

// trimming/padding is fiddly -- simplify: just use a Bezier-like single-span cubic through
// control points directly (poles = pts), degree = npts-1, single span (clamped).
TColgp_Array1OfPnt poles(1, npts);
for (int i = 1; i <= npts; i++) poles.SetValue(i, pts.Value(i));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supplementary finding (a re-run of /code-review --comment on this same commit surfaced this; it's additive to review #4880045383 above, not a duplicate of its finding #2 — that one flagged the dead multi-span knot vector and the mislabeled "cubic" comment in this same function, this one is about the resulting curve's fidelity).

wavyEdge()'s poles here are the raw sine-sampled points (line 47), and with k2/m2 at line 49-50 giving a single span at full multiplicity (npts), the resulting curve is mathematically a degree-npts-1 (13, for npts=14) Bézier curve through those points as control points, not an interpolant. High-degree Bézier curves substantially damp high-frequency oscillation in their control polygon — the curve doesn't pass through its poles, so the realized amplitude is well below the intended amp=4.0, 3-wave shape the comment above (line 30-32) describes.

This matters because the README/PR cite this script's G0Error-vs-tolerance measurements as evidence that a "genuinely-difficult but not-absurd boundary" stays within the bridge's default tolerance — but the boundary actually tested is smoother (lower effective amplitude/curvature) than the sine-wave shape the code intends to construct, so it's weaker evidence for that claim than the prose implies. occt_597_fill_g0_mechanism.mm's proper Bézier-boundary construction is the cleaner demonstration of the tolerance-exceeding mechanism, per the original review's own note.

(Line cited above is corrected from the tool's original report, which pointed at line 20 — the t parameter calc, unrelated to this issue.)

@gsdali

gsdali commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Both reviews picked up and adjudicated. All five findings stand, and the second review's is the one
that matters: it weakens this PR's own evidence, and I can quantify it.

The supplementary finding, measured

wavyEdge() builds k2/m2 at multiplicity npts on both ends, so the curve is a single-span
degree-13 Bezier and the sine-sampled points are its control points, not points it passes
through. Computing the resulting curve directly from the Bernstein basis, for the exact parameters
the script uses (amp=4.0, waves=3, npts=14):

Intended amplitude 4.0
Control-point peak 3.971
Realized curve peak 1.490
Damping 62.8% of the intended amplitude is gone
Intended zero crossings 6
Realized zero crossings 5

So the boundary is not merely smoother than described, it has lost most of its amplitude and one
of its lobes
. The README and PR body cite this script's G0Error-versus-tolerance numbers as
evidence that a "genuinely difficult but not absurd" boundary stays inside the bridge's default
tolerance. That claim is currently supported by a boundary at 37% of the amplitude the code intends
to build.

The conclusion may well survive. occt_597_fill_g0_mechanism.mm is the cleaner demonstration and
the original review says so. But the fix is to build a real interpolant, re-measure, and report
whatever comes out, not to soften the prose around a fixture that does not do what its name says.
That is #723's shape exactly, and this repo now has a policy about it.

The other four

  1. Hand-typed census table. Valid. Cluster E already has an executable census at
    Scripts/repro/572-approx-consumer-sweep/, which is GeomFill_Sweep.cxx overwrites a measured approximation error with the requested tolerance #597's own origin, and docs/v2.0.0-plan.md
    requires consuming it rather than re-deriving. Point at it.
  2. Dead multi-span knot vector, plus "cubic" naming a degree-13 curve. Confirmed by reading:
    knots/mults are built and never referenced, and the comment mislabels the result. Same
    function as the finding above.
  3. The rejected-fix record lives only in two inline comments. This is the substantive one of the
    four. Break down Document.swift (17,603 LOC): only 14% of it is Document, evict the rest first #393 to Break down OCCTBridge.h (21,896 LOC) into 16 per-domain headers: the split is fully derived, 4021 symbols, 0 ambiguous #395 will move these files, and a comment carries no signal through a file
    breakdown. It belongs in CLAUDE.md's Known OCCT Bugs, which is the grep-from-one-place record.
  4. Dead nKnots, off by one from what is used. Confirmed, different file from finding 2.

Dispatched.

gsdali and others added 2 commits August 8, 2026 04:01
…erpolant

PR #751 review, finding 5: wavyEdge() in occt_597_fill_g0_realistic.mm handed
its sine-sampled points to Geom_BSplineCurve as a single-span, full-multiplicity
curve, i.e. as control poles of a degree-13 Bezier, damping the intended
amplitude by 62.8% (independently verified via the Bernstein basis). Rebuilt on
GeomAPI_Interpolate, matching this repo's own naming precedent
(OCCTCurve3DInterpolate). Re-measured G0Error now exceeds even the bridge's own
default tolerance by ~2000x, and the accepted surface's poles land ~530 units
from a ~10-unit boundary; the PR's conclusion survives and is sharpened, not
reversed, since the load-bearing evidence (the FillingSupportFaceTests
regression) is independent of this fixture.

Also: findings 1-4 from both reviews. Point the repro README at Cluster E's own
census artifact (Scripts/repro/572-approx-consumer-sweep/) instead of
duplicating its table, and extend that artifact with the wider family #597
established. Removed the dead nKnots variable in
occt_597_bsplinerestriction_selfpolice.mm. Added a CLAUDE.md Known OCCT Bugs
entry recording the "gating on ApproxError()/G0Error() regresses shipped
behaviour" investigation durably, ahead of the pending file breakdowns
(#393-#395) that would otherwise carry no signal past the inline comments.

No production code changed. All 5 gate scripts and self-tests green, full
swift test (5442 tests) green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gsdali

gsdali commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

The fixture rebuild is right and the re-measurement is the valuable part. One thing sent back.

The corrected fixture falsifies the original measurement, and that is fine

GeomAPI_Interpolate in place of the single-span full-multiplicity Bezier, and the boundary that
previously read G0Error=5.9e-05 at tol3d=1e-4 now reads 0.204, exceeding the bridge's own
default by roughly 2000x. The added bounding-box diagnostic is what makes it interpretable: the
accepted surface's poles sit around 530 units from a 10-unit boundary, so this is a diverged fit
rather than a demanding one.

That is the opposite of what the README used to claim, and saying so plainly rather than softening
it is the right call.

What I sent back

The README now argues:

there is no single threshold on G0Error()'s raw value that rejects this case while keeping those
two passing

That claim is unmeasured. curvatureContinuityIsAccepted and internalConstraintIsNotABoundary
are named, but their own G0Error values appear nowhere on the branch. The nearby figures,
6.22713e-05 and 9.03801e-07, belong to occt_597_fill_g0_mechanism.mm, a different fixture, and
cannot stand in for them.

It is also load-bearing. It is the sentence that turns "gating on the caller's tolerance is unsafe",
which is proven by the 2-of-17 regression, into "no threshold can work at all", which is not. If the
two regressing tests sit four orders of magnitude below 0.204, as the mechanism fixture's numbers
hint they might, then a fixed absolute threshold would separate them and the honest conclusion
is narrower: unjustified rather than impossible.

Narrower is still sufficient. "The obvious gate is proven unsafe, and any other threshold would be a
number nobody measured" is a complete argument for not shipping one, and it is #726's own reasoning
turned on ourselves. The overstatement is what I want gone, not the conclusion.

The measurement is cheap: the gating patch that produced the 2-of-17 regression was already written
and run, so printing G0Error() alongside the effective tolerance per test is a small edit to
existing work.

Noting the pattern

This is the third time today an unmeasured claim has turned up inside work whose subject is
unmeasured claims: the census script citing hasExtent as its model of correct code, my own stray
-modification parser missing 0020 because it keyed on one diff header spelling, and now this. The
common shape is not carelessness, it is that a number adjacent to the one you need reads as the
one you need
.

Everything else in the response is good. Finding 2 is correctly moot, the census now points at
Cluster E's own artifact rather than re-deriving, and the CLAUDE.md entry is ordered against the
kernel half's entry that landed mid-task.

gsdali and others added 2 commits August 8, 2026 04:24
A second coordinator review caught that the "no single G0Error() threshold
can separate a diverged fit from the two regressing FillingSupportFaceTests"
claim was itself unmeasured: curvatureContinuityIsAccepted's and
internalConstraintIsNotABoundary's own G0Error values appeared nowhere on
the branch.

Measured them: temporary debug prints in OCCTShapeFillBuildResult and
OCCTFillingBuild (both BRepOffsetAPI_MakeFilling::Build() call sites), each
of the 17 FillingSupportFaceTests run individually via `swift test --filter`
so every printed value maps to one test unambiguously, then reverted (no
production code changed, confirmed via git diff and a clean rebuild).

The two regressing values are 5.295e-4 (5.3x the 1e-4 tolerance every test
uses) and 1.231e-3 (12.3x tolerance), two to three orders of magnitude below
the corrected wavyEdge fixture's ~2000x-2260x. A fixed absolute threshold
between them would in fact separate today's known cases. Narrowed the claim
in the repro README and the CLAUDE.md/PR-body copies accordingly: gating on
the caller's own requested tolerance is unsafe and proven so by the two real
regressions; a fixed threshold is unjustified rather than impossible, since
nobody has measured what it should be, which is what #726 exists to catch.
Does not change the SemVer impact or the conclusion about the two bridge
sites, per instruction, since the numbers did not force either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…dashes

Two corrections to the repro README, both surfaced by measuring the threshold claim.

**The count.** The gating experiment patched `OCCTShapeFillBuildResult` only, but there are two
`Build()` call sites: `OCCTFillingBuild` is the `FillingSurface` entry point #434 converged onto the
same builder, and it went ungated. `fillingSurfaceContinuityMappingIsCorrect` reaches it and exceeds
tolerance by the same 5.3x, so a complete gate breaks three tests, not two. The measured table
already recorded this in a row; the paragraph a reader actually lands on did not.

It strengthens the conclusion rather than weakening it, which is presumably why it read as a detail.
An undercount in your favour is still an undercount, and #434's whole point was that these two entry
points had been reaching for the same fixes independently.

**The em-dashes.** Fourteen, in a file that does not exist on the base branch, so all new work under
a standard that forbids them. The one remaining `--` is inside a captured program transcript, where
editing the output would be worse than the inconsistency.
@gsdali

gsdali commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Measured, and it is outcome 2. The claim was overstated and is now narrowed.

G0Error multiple of tolerance source
1.33e-15 ~0 positional-only fill
1.43e-05 0.14x plain tangent fills, 9 calls
2.77e-05 0.28x flat / boundary-only fills, 4 calls
5.30e-04 5.3x curvatureContinuityIsAccepted, regresses
1.23e-03 12.3x internalConstraintIsNotABoundary, regresses
0.204 ~2000x the corrected diverged fixture

The two regressing tests sit two to three orders of magnitude below the diverged case, not
beside it. A fixed absolute threshold anywhere in the gap between 1.23e-03 and 0.204 separates
them cleanly. So "no threshold can work" was wrong, and the README, the CLAUDE.md entry and the PR
body now say the defensible thing instead: gating on the caller's requested tolerance is unsafe
and proven so by two real regressions, while a fixed threshold is unjustified rather than
impossible, because nobody has measured what it should be. Which is #726 turned on ourselves.

That is still a complete argument for not shipping a gate. It is just an argument that survives
someone checking it.

The bonus finding is the better one

The measurement turned up that 2 of 17 was itself an undercount. The gating patch touched
OCCTShapeFillBuildResult only, and there are two Build() call sites: OCCTFillingBuild
(OCCTBridge_Modeling.mm:4476) is the FillingSurface entry point #434 converged onto the same
builder
, and it was never gated. fillingSurfaceContinuityMappingIsCorrect reaches it and exceeds
tolerance by the same 5.3x, so a complete gate breaks 3 of 17.

I have pushed that into the paragraph a reader actually lands on, since the measured table recorded
it in a row where it read as a detail. An undercount in your own favour is still an undercount, and
#434 exists precisely because those two entry points kept reaching for the same fixes independently.
This is the third time that split has cost something.

Also removed fourteen em-dashes from the repro README. The file does not exist on the base branch,
so all of it is new work under a standard that forbids them. The single remaining -- sits inside a
captured program transcript, where editing the output would be worse than the inconsistency.

Ready for your review.

@gsdali
gsdali merged commit 0e267dd into refactor/381-pass1b Aug 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants