test(guards): fail the build on the documentation drift this line kept re-fixing - #478
Merged
Conversation
…t re-fixing Four releases of hand-corrections shared one property: nothing would have caught any of them. The guards below close that, each one keyed to a defect that actually shipped. Link liveness reads what the documents point at, so it needs no token list and cannot go stale. It found nine dead source links in implementation-guide.md on its first run. Retired-surface scanning covers README, CONTRIBUTING, SECURITY, SUPPORT, ROADMAP, the examples gallery, docs/ and .github/ — the last four sat outside every existing guard, which is how an issue template could route reporters to a theme class removed in 2.0. The token list is what verification supports: seven types absent from every src/main tree. BusinessTheme and PptxSemanticBackend are deliberately absent from it — both are alive, in 21 and 2 source files, and forbidding them would fail the build on correct text. Historical records are skipped by path prefix rather than a file allowlist, so a new page under docs/archive/ is covered the day it lands. DocsBoldFaceGuardTest pins the font rule: a *_BOLD constant resolves to its base family and the face comes from the decoration, so naming the alias without one renders regular. Its self-check counts every font selection rather than every alias — counting aliases made a clean tree look like a broken scan, because the correct end state has none. The release-status assertion requires a published version and requires the link to point at the tag the text names, so a half-updated block cannot pass. implementation-guide.md loses the two sections built on the execution layer 2.0 removed, and the render-contract references that went with them: Render, RenderPassSession, RenderStream, TextComponent and BlockText are absent from every src/main tree. Deleting them makes the guide less wrong; describing the real pipeline in their place is still open.
…on layer Deleting the dead names from implementation-guide.md left the model behind. The document still walked a contributor through attaching components to an entity, adding a render marker and a container-growth marker, implementing Breakable, registering through build() and checking entity.hasRender() — forty lines of a pipeline 2.0 removed, and one CONTRIBUTING now contradicts point for point. Breakable, ParentComponent and hasRender are absent from every src/main tree. It is archived rather than rewritten. extension-guide.md already walks the live pipeline end to end — a semantic node, a fluent setter, a render handler, a whole backend, snapshot tests — and package-map.md covers the layout; inventing a replacement for an engine guide risks shipping something plausible and wrong, which is worse than pointing at the documents that are correct. The archived copy opens with what it describes, what removed it, and where to go instead. Breakable, ParentComponent and hasRender( join the retired-token list, so the model cannot return to a live document. Two guards were weaker than the defects they were written for. The retired-token scan could not catch BusinessTheme, because the name survives as an examples-local helper and a repository-wide existence check cannot tell "this type is gone" from "this type is an example's private business". The question that matters to a reader is whether a type ships in a published artifact, so API guidance — issue and pull-request templates, CONTRIBUTING, the template docs — is scanned against its own list. examples/README.md is deliberately outside it: the helper it documents is real. The font guard accepted an alias paired with any decoration, so HELVETICA_BOLD with decoration(ITALIC) passed while rendering neither bold nor what the constant claims. Published snippets now name the family outright; the rule has no exception to get subtly wrong, and the builder-chain analysis it needed is gone. The link check now reads .github too. The retired-token scan already covered it, while the issue templates carry the relative links most likely to break silently.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Four pull requests in this series corrected documentation, and not one of the defects would have been caught by anything. The guard job scans a fixed token list over four roots;
SECURITY.md,SUPPORT.md,ROADMAP.mdand.github/are outside every existing check. That is how an issue template could route reporters to a theme class removed in 2.0, and how a contributor guide could keep nine links to source files that no longer exist.Each guard here is keyed to a defect that actually shipped, and each one found something on its first run.
What changed
Link liveness (
CanonicalSurfaceGuardTest). Every relative link in the public docs must resolve on disk. It needs no token list and cannot go stale — it reads what the documents point at. First run: nine dead links indocs/contributing/implementation-guide.md.Retired-surface scanning, over a root set widened to
SECURITY.md,SUPPORT.md,ROADMAP.md,.github/and the examples gallery. The token list is what verification supports and no more: seven types absent from everysrc/maintree.BusinessThemeandPptxSemanticBackendare deliberately not on it — both are alive, in 21 and 2 source files respectively, and forbidding them would fail the build on text that is correct. Historical records are exempted by path prefix in code rather than a file allowlist, so a new page underdocs/archive/is covered the day it lands instead of failing the build until someone remembers to allowlist it.DocsBoldFaceGuardTest(qa) pins the font rule the previous PR fixed by hand:FontLibraryresolves every*_BOLD/*_ITALIC/*_OBLIQUEconstant back to its base family, so a documented style naming the alias without adecoration(...)renders regular. The failure is silent — the snippet compiles, runs, produces a PDF, and the text is simply the wrong weight.Release-status assertion (
VersionConsistencyGuardTest). The block must name a published version and link the tag it names, so a half-updated block cannot pass.implementation-guide.mdis archived. Removing the dead names from it left the model: it still walked a contributor through attaching components to an entity, adding a render marker and a container-growth marker, implementingBreakable, registering throughbuild()and checkingentity.hasRender()— forty lines of a pipeline 2.0 removed, whichCONTRIBUTING.mdnow contradicts point for point.Breakable,ParentComponentandhasRenderare absent from everysrc/maintree.It moves to
docs/archive/rather than being rewritten.extension-guide.mdalready walks the live pipeline end to end — a semantic node, a fluent setter, a render handler, a whole backend, snapshot tests — andpackage-map.mdcovers the layout. Inventing a replacement for an engine guide risks shipping something plausible and wrong, which is worse than pointing at the documents that are correct. The archived copy opens with what it describes, what removed it, and where to go instead; all six inbound links are repointed.Breakable,ParentComponentandhasRender(join the retired-token list so the model cannot come back to a live document.API guidance is scanned against its own list. The retired-token scan could not have caught the defect that motivated it:
BusinessThemesurvives as an examples-local helper, so a repository-wide existence check cannot distinguish "this type is gone" from "this type is an example's private business". The question that matters to a reader is whether a type ships in a published artifact. Issue and pull-request templates,CONTRIBUTING.mdand the template docs are checked againstFORBIDDEN_IN_API_GUIDANCE;examples/README.mdis deliberately outside it, because the helper it documents is real.The font guard refuses the alias outright. Requiring alias-plus-decoration still accepted
HELVETICA_BOLDwithdecoration(ITALIC)— neither bold nor what the constant claims — and kept publishing a form that reads as the weight set twice. Published snippets name the family; the rule has no exception to get subtly wrong, and the builder-chain analysis it needed is gone.The link check reads
.githubtoo. The retired-token scan already covered it while the link check stopped atdocs/, and the issue templates carry the relative links (../../docs/api-stability.md) most likely to break silently.Verification
Reactor gate
BUILD SUCCESS, 13/13 modules, 2:04, zero errors. Core 412 → 416 tests, qa 681 → 682.javadoc:javadocexits 0.Both new guards were exercised in both directions rather than only observed passing:
docs/contributing/implementation-guide.md names EntityBounds(first run, before the doc fix)implementation-guide.md -> …entries (first run)docs/getting-started.md selects FontName.HELVETICA_BOLD— injected with a matchingdecoration(BOLD), the form the first version of this guard accepted.github/ISSUE_TEMPLATE/feature_request.md offers BusinessTheme— injected by reverting #476's fix, then restoredThe token list was built by checking each candidate rather than from memory: of ten, seven are absent from every
src/maintree and three are alive.The bold-face guard's own self-check was wrong first. It counted alias sites and failed at zero — but zero alias sites is the goal, and #475 produced exactly that, so a clean tree read as a broken scan. It now counts every
fontName(FontName.*)selection, which still catches a scan root that moved without misreading success as failure.Not in scope
Two guards named in the previous PR's follow-ups are not here, and are not dropped:
DocumentationSnippetCompileTestto the root and module READMEs needsgraph-compose-render-docxat test scope inqa(otherwisenew DocxSemanticBackend()inrender-docx/README.mdfails to resolve and CI goes red for the wrong reason) plusdoc-examplemarkers across ten files.ShowcaseMetadatacoverage assertion needs that package-private class's visibility changed.Both are self-contained changes; this one already carries three guards and the documentation cleanup they forced.
The link check skips historical records, so a dead link inside an archived page or a migration guide still ships. That is the same exemption the token scan uses and it is deliberate — those documents point at things that no longer exist by design — but it is a hole, not a covered case.
Writing an accurate engine guide to replace the archived one is still open — it needs the pipeline explained by someone who built it, not paraphrased from the package layout.
PackageMapGuardTestcompleteness overdocument/backend/**— asserting every package with apackage-info.javais named in CONTRIBUTING and the package map — would have caught thebackend.fixed.pptxomission #476 fixed by hand. It is not implemented here.