docs(#802): existence gate, exhaustive snippet check, five behavior pages corrected - #804
Conversation
…behavior pages corrected Item 1: new Scripts/check-docs-existence.py verifies every symbol docs/ documents as current API still exists in Sources/OCCTSwift, keyed on (owning type, member name) so a rename cannot collide with a same-named live symbol (ShapeContentsExtended.nbEdges vs the removed Shape.nbEdges). Found and fixed 14 real stale references across 8 pages: three sections (BSplineContinuity, Continuity, ContinuityOrder) still described removed typealiases as live, Document-OCAF-Attributes.md's whole PathParser section documented an entirely removed type, and five pages named wrong method signatures unrelated to #784 (Curve3D.helix, Shape.fillet, Shape.pipe, Mesh.fromShape/from, Curve2D.rectangle/arc). Wired into ci.yml's gate-scripts job and the optional pre-commit hook alongside the other five gates. Item 2: measured the existence-check's own machinery against all 7,294 ```swift snippets (Scripts/repro/802-snippet-sample/), exhaustively rather than by sample since the check is text matching, not compilation. Found and fixed 4 more stale snippets (Curve3D.helix, Exporter writeGLB/exportDXF, Curve2D.rectangle, Mesh.fromShape/from). A 40-snippet real-swiftc sample confirmed 0 defects beyond existence once extraction artifacts (this repo's own body-less signature restatement convention) are correctly classified, closing the case for exhaustive per-snippet compilation. Item 3: read all five behavior-changed APIs against their fix PRs. detectPocketsAAG() and detectHoles() were badly stale (describing pre-#762/#747 criteria entirely); rewrote both sections. ShapeAxis.extent, Document.shapeColor and AAGEdge.sharedEdgeCount were already accurate; added brief historical context to the last. Item 4: count-operations.py confirms README/API_REFERENCE totals match (4256). Item 5: measured a sample of the 97 newly-visible functions from #797's parser fix; found real (pre-existing) reference-page gaps. Decision recorded in the PR body: not release-blocking. Also fixed two real parser bugs in check-docs-existence.py found against the real corpus: `indirect enum` and dotted `extension Type.Nested` were invisible to the type tracker, and protocol-extension-supplied members (ArcLengthCurveAdaptor.maximumSampleCount) were invisible to conforming types until a conformance-resolution pass was added.
|
Reviewed. Nothing blocking. I verified the gate against the live tree rather than trusting its The gate discriminates, proven on the real corpusInjected two references into It caught the removed one and did not flag the live collision. That is the exact trap I warned Exit code is 1 with a stale reference and 0 when clean, so it fails CI rather than merely reporting. The convergence is the part worth reading946 candidates, then 127, then 14 real. Most reviewers would have shipped the first number as a A checker that cries wolf at 946 gets ignored, which would have been worse than not having one, The 14, and what they say about the sweep before themTwo of the fixes are the ones that matter: Five more were wrong method names unrelated to the deprecation work ( Snippets: measured rather than sampled, and the right call7,294 checked exhaustively, because text-matching is cheap and only compilation is not. Four more Item 5, and one thing I want on the record13 of a 35-function sample are undocumented across types the README's own Status table calls But it means the Status table overclaims, and that is a separate, smaller problem from the one this AlsoFinding and fixing a real conformance-resolution bug, protocol-extension members being invisible to And you swept roughly 50 em-dashes out of your own new prose after introducing them. Recording that |
…re-guessed The gate walks docs and checks each reference resolves. `--coverage` walks the source and checks each member is referenced, which is the inverse question and the one item 5 asked. A clean gate run says nothing about coverage, so they are separate modes rather than one number. Measured: **1,487 of 6,471 public members, 23.0%, are named nowhere in `docs/`**, across 447 types. By declaration kind that is 720 properties, 575 enum cases, 159 functions and 33 other, which is a very different shape from a flat list and changes what completing it means: one table covers `Shape.CheckStatus`'s 37 cases, where 159 functions want prose and snippets. **This lives here because deriving it any other way produced a wrong answer.** An ad hoc regex over `Type.member` and `member(` reported 1,857, roughly 25% high, because a member documented as a bare heading or in running prose is invisible to that shape. `Shape.isValidSolid` is documented in nine files and that count called it missing. The number was believed and nearly acted on before a five-item spot check killed it. This script already parses docs correctly, handles the `Shape.nbEdges` versus `ShapeContentsExtended.nbEdges` collision, resolves protocol conformances and recognises historical annotations, so it is the only place the question can be asked honestly. Never fails. Coverage is a backlog, not a property of a correct tree, and a gate that failed on it would fail every build until the backlog is empty. Four self-test cases with a removal matrix, each isolating one mechanism. The first is the one that matters: `rep.seen` is populated as a side effect of the staleness walk, so a change that stopped recording it would leave every existing case green while coverage silently reported the entire surface as undocumented.
What & why
#802: a merge-to-main condition for v2.0.0. The release removes 62 deprecated symbols (#784/PR
#798), makes 97 functions visible to tooling for the first time (PR #797's parser fix), and
changes behavior under five unchanged signatures. Nothing in the repo's existing gate scripts
checks that a documented symbol still exists, and during #798 that gap let two reference pages
show live-looking signatures for deleted symbols, caught only by a reviewer reading closely. This
PR builds the check, runs it, fixes every stale reference it (and a snippet-level extension of it)
found, verifies the five behavior-changed pages, confirms the operation-count gate, and records
the decision on the 97 newly-visible functions.
Closes #802
Item 1: existence, the highest-value half
New
Scripts/check-docs-existence.py, wired intoci.yml'sgate-scriptsjob and the optionalpre-commit hook alongside the other five gates. Every symbol
docs/**/*.mdandREADME.mddocument (excluding
docs/CHANGELOG.md/docs/SEMVER.md, append-only historical ledgers) ischecked against a live index built from
Sources/OCCTSwift, keyed on the exact (owning type,member name) pair so a rename can never collide with a same-named live symbol elsewhere: the
ShapeContentsExtended.nbEdgesvs. removedShape.nbEdgestrap the issue calls out is adedicated self-test case, proven both directions.
11 self-test cases, each a real removed/renamed symbol from PR #798's own migration table or one
of the two named collision traps, run once with the relevant guard disabled per
okf/policies/prove-the-test-fails.md(documented in the self-test's own comments; theprotocol-extension case's removal drops the battery from 11/11 to 10/11, isolated to exactly that
case).
Measured against the real tree, then iterated three times before converging clean:
candidates; reading a sample showed the guess wrong nearly every time, because the real pages
mix a genuine type-name heading (
## AAG) with a// MARK:-style group label (## Properties,reused verbatim across many files) under the identical un-backticked convention, and several
pages are grab-bag completions pages whose filename names none of the types they document.
Fixed by asking existence globally for a bare (undotted) reference, while keeping every
dotted reference (
Type.member) strict, since that is the shape the actual trap lives in.indirect enumandextension Shape.Historywith a dottedname were both invisible to the type tracker) and an overly strict access-level gate
(
Face.exactBoundsisinternal,FeatureRecognition.swift'sbuildGraph()isprivate,both real and both correctly documented for context) accounted for most of it.
docs/reference/Curve3D-Analytic-Types.md,Shape-Measurement.md,Surface.md: threesections (
ContinuityOrder,BSplineContinuity,Continuity) still showed a live@available(*, deprecated)typealias declaration and said "is now a typealias of X"; Merge-to-main condition: adjudicate all 61 deprecations, and rescan Pass 1a/1b for duplication those passes missed #784removed the typealias entirely. Rewritten to say so, matching the style already used for the
already-fixed
BRepGraph.generationentry.docs/reference/Document-OCAF-Attributes.md: the wholeTDocStd_PathParsersectiondocumented
PathParser.trek/.name/.fileExtension, a type Merge-to-main condition: adjudicate all 61 deprecations, and rescan Pass 1a/1b for duplication those passes missed #784 removed outright. Rewrittento point at
OSDPath, which already covers the same ground plus more.docs/guides/cookbook/brep-graph-uids.md: a "do not use it" guide section still describedgenerationas merely deprecated rather than removed.Curve3D.helix(radius:pitch:turns:)(real:Curve3D.circularHelix(radius:pitch:), threesites),
Shape.fillet(edges:radius:)(real:filleted),Shape.pipe(profile:path:)(real:pipeShell(spine:profile:), three sites inWire.md),Mesh.fromShape/Mesh.from(Meshhas no such factory; real:
Shape.mesh(...)),Curve2D.rectangle/Curve2D.arc(real:4-segment construction /
arcOfCircle),Shape.makeFace/makePolygon(real:Shape.face(from:)/
Wire.polygon3D),Exporter.writeGLB/Exporter.exportDXF(real:writeGLTF(..., binary:)/DXFExporter.writeDXF).Final state:
0 stale,29 acknowledged historical(mentions a removed name and correctly saysso),
0 unresolved.Item 2: snippets, measured then scoped
7,294
```swiftfenced blocks in scope. Measured, not sampled first: the check that matters(does a snippet name a removed symbol) is text matching against an already-built index, not
compilation, so it costs nothing to run against the full population.
Scripts/repro/802-snippet- sample/measure-snippet-failures.pyreusescheck-docs-existence.py's own machinery, nowconformance-aware (see below), against every snippet's code text.
First pass found 16 candidates; two were false positives from this checker's own gaps (
.selfmetatype references, and
WireCurve/EdgeCurve.maximumSampleCount, which is genuinely inheritedfrom a protocol EXTENSION on
ArcLengthCurveAdaptorwith no separate declaration near eitherconforming type for the checker to find). Fixed the second properly:
check-docs-existence.pynow tracks textual conformance declarations and resolves a member through them, transitively, with
a dedicated self-test proving it load-bearing. The other 14 candidates were real (4 new beyond
item 1's list, since a snippet's own code can name something a heading never restates:
Curve3D.helixagain,Exporter.writeGLB/exportDXF,Curve2D.rectangle,Mesh.fromShape/from), all fixed. Final exhaustive run: 0/7294.Then, separately, a real-compilation sample: 40 random snippets fed through
swiftc -typecheckagainst the built
OCCTSwiftmodule. All 40 failed to typecheck, and all 40 are extractionartifacts, not docs defects, once classified: this repo's own reference-page convention of
restating just a signature with no body (
public var shellCount: Int { get },public enum ProjectionSymbolwith no cases) is invalid Swift at file scope, and a bare fragment(
graph.setCoEdgeUVBox(...)) assumes agraphthe surrounding prose bound, not the snippetitself.
0failures remained once fragment-shaped errors are excluded. This closes the case forexhaustive per-snippet compilation: the existing extraction shape makes it measure the extraction
method, not the docs, for the overwhelming majority of the corpus.
Item 3: the five behavior-changed APIs
Read each against its fix PR:
detectPocketsAAG()(detectPocketsAAG() cannot see a pocket with a filleted floor/wall junction, which is most real pockets #762) anddetectHoles()(AAG.detectHoles() reports zero holes for an ordinary blind or through cylindrical hole #747) were both badly stale, still describingtheir pre-fix criteria in full (direct-concave-neighbor-only wall discovery; all-concave-
adjacency plus a bounding-box aspect-ratio heuristic). Both sections rewritten in
FeatureRecognition.mdto describe the current mechanism (fillet/chamfer junction absorption;surface-type + closed-in-U + radially-inward-material criterion).
ShapeAxis.extent(Unmeasured values, production half: adjudicate the census's 62 bridge candidates #763): already accurate (docs/reference/Geometry2D.mdwas updated in PRfix(#763): triage core bridge files' unmeasured-value candidates #770 itself).
Document.shapeColor(Unmeasured values, production half: adjudicate the census's 62 bridge candidates #763): already accurate (docs/reference/Document-Persistence-IO.mdwasupdated in PR chore(#763): triage Document.mm + IO.mm's 26 unmeasured-value candidates #768 itself).
AAGEdge.sharedEdgeCount(AAG's hand-rolled face/edge adjacency duplicates BRepGraph's outerWire(of:)/sharedEdges(between:and:) #761): already accurate for current behavior (never claimed a cap);added one sentence of historical context (was silently capped at 10 before v2.0.0) since the
page otherwise gave no indication anything had changed.
Item 4: confirmation
count-operations.py: README headline 4256, API_REFERENCE total 4256, both match the derivedcount. Clean, as expected since this PR touches no
Sources/file.Item 5: the 97 newly-visible functions, decided
Not release-blocking, per the issue's own framing, confirmed rather than assumed. I could not
reproduce PR #797's exact 97 (that count is intrinsic to its own duplication-detector's tokenizer,
a different, narrower population than "has a nested-paren parameter"), so I built a smaller,
independent sample of 35 public functions with a tuple-typed parameter using the same live index
this PR already has. 13 of 35 (37%) have no reference-page entry anywhere in
docs/reference/,spread across
Shape,BRepGraph,Camera,ClipPlane, andLawFunction, every one of whichdocs/reference/README.md's own coverage tracker currently marks "done". These are pre-existinggaps (always public, never independently inventoried by anything until #797's parser fix), not a
regression from this release.
Decision: not release-blocking for v2.0.0. Documenting them is real, boundable follow-up work,
not urgent enough to hold the release for. Recommend a follow-up issue scoped to (a) closing the
13+ measured gaps and (b) correcting
docs/reference/README.md's Status table, which currentlyoverclaims complete coverage for types that have measured gaps.
Tests
This PR touches no
Sources/OCCTSwiftorSources/OCCTBridgefile, only documentation and newstandalone tooling under
Scripts/. Fullswift test: 5463 tests, 1436 suites, 0 failures,one clean run (not three, since nothing in
Sources/changed and a docs/tooling-only diff carriesnone of the risk the three-run recommendation in #786 is aimed at).
All ten scripts under
Scripts/*.py(the nine pre-existing plus the newcheck-docs-existence.py), each run plus its--self-testwhere it has one:census-unmeasured-values.py's bare run (a census, not a gate) andcheck-changelog- transcription.py's bare run (a report, not a gate) are both unaffected in character by thisdiff, as expected for a docs/tooling-only change.
Prove the test fails, per
okf/policies/prove-the-test-fails.md: the new self-test'sprotocol-conformance case (proving
check-docs-existence.pyresolves a member supplied only by aprotocol extension, the
WireCurve/ArcLengthCurveAdaptorshape) was run once withresolve_conformancesneutered to a no-op. Result: 10/11 passed, failing exactly and only the newcase (
expected clean=0, got stale=1), restored, re-ran clean at 11/11.CHANGELOG entry
A new gate checks that every symbol
docs/documents as current API still exists inSources/OCCTSwift(#802)Scripts/check-docs-existence.pycloses a gap none of the existing gates covered: during #798,two reference pages showed a live-looking signature for a symbol the same PR had deleted, caught
only by a reviewer reading closely. The new gate checks every symbol
docs/**/*.mdandREADME.mddocument againstSources/OCCTSwift, keyed on the exact (owning type, member name)pair so a rename cannot collide with a same-named live symbol elsewhere (
Shape.nbEdges, removed,vs.
ShapeContentsExtended.nbEdges, live and unrelated). Wired intoci.yml'sgate-scriptsjoband the optional pre-commit hook alongside the other five gates.
Fixed 18 stale references it found: three sections (
Curve3D.ContinuityOrder,Shape.BSplineContinuity,Surface.Continuity) still described a removed typealias as a livedeprecated alias;
Document-OCAF-Attributes.md's wholeTDocStd_PathParsersection documented atype #784 removed outright (now points at
OSDPath); a cookbook guide still calledgenerationmerely deprecated rather than removed; and 13 pages (headings and code snippets both) named a
method that never existed under that name, unrelated to #784:
Curve3D.helix,Shape.fillet,Shape.pipe,Mesh.fromShape/Mesh.from,Curve2D.rectangle/Curve2D.arc,Shape.makeFace/makePolygon, andExporter.writeGLB/exportDXF.Also corrected
docs/reference/FeatureRecognition.md'sdetectPocketsAAG()anddetectHoles()sections, both of which still described their pre-#762/#747 criteria in full rather than the
current mechanism.
SemVer impact
NONE. This PR changes no public Swift declaration: it adds a new internal gate script and repro
tooling under
Scripts/, and corrects documentation prose and code examples to match already-shipped behavior. Nothing in
Sources/OCCTSwiftorSources/OCCTBridgechanged.Checklist
--self-test(11 cases) is that test, per this repo's convention for gate scripts.--self-testcase was run once with its subject broken, andthe failure is reported above (see "Prove the test fails").
docs/CHANGELOG.mdis not in this diff.docs/SEMVER.mdis not in this diff.Notes for the reviewer
refactor/381-pass1bfor reviewonly.
Sources/OCCTSwift/{DXFExporter, DrawingDispatch,PDFExporter,SVGExporter}.swiftandTests/OCCTIOTests/. This PR does not touchany of those four source files or that test directory; it does correct two documentation
mentions of
Exporter.writeDXF/writeGLTF(real, existing methods on theExporterextensionin
DXFExporter.swift, not a change to that file itself).per the issue's own instruction; happy to file the recommended follow-up issue if that is
wanted, left undone here since the task was to decide and record, not to also open new tracking
issues.
docs/occt-upgrades.mdis in the existence check's scope (prose about OCCT kernel versionhistory, not excluded like
CHANGELOG.md/SEMVER.md); it produced no findings.Scripts/repro/802-snippet-sample/measure-snippet-failures.py)is a one-off measurement artifact per the issue's "sample first, decide" framing, not proposed
as a permanent gate;
check-docs-existence.pyitself is the permanent artifact.