Skip to content

test(guards): fail the build on the documentation drift this line kept re-fixing - #478

Merged
DemchaAV merged 2 commits into
developfrom
test/doc-drift-guards
Jul 31, 2026
Merged

test(guards): fail the build on the documentation drift this line kept re-fixing#478
DemchaAV merged 2 commits into
developfrom
test/doc-drift-guards

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Jul 31, 2026

Copy link
Copy Markdown
Owner

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.md and .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 in docs/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 every src/main tree. BusinessTheme and PptxSemanticBackend are 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 under docs/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: FontLibrary resolves every *_BOLD / *_ITALIC / *_OBLIQUE constant back to its base family, so a documented style naming the alias without a decoration(...) 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.md is 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, implementing Breakable, registering through build() and checking entity.hasRender() — forty lines of a pipeline 2.0 removed, which CONTRIBUTING.md now contradicts point for point. Breakable, ParentComponent and hasRender are absent from every src/main tree.

It moves to docs/archive/ rather than being 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; all six inbound links are repointed. Breakable, ParentComponent and hasRender( 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: BusinessTheme survives 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.md and the template docs are checked against FORBIDDEN_IN_API_GUIDANCE; examples/README.md is deliberately outside it, because the helper it documents is real.

The font guard refuses the alias outright. Requiring alias-plus-decoration still accepted HELVETICA_BOLD with decoration(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 .github too. The retired-token scan already covered it while the link check stopped at docs/, 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:javadoc exits 0.

Both new guards were exercised in both directions rather than only observed passing:

Guard Clean tree Defect reintroduced
Retired surface green docs/contributing/implementation-guide.md names EntityBounds (first run, before the doc fix)
Link liveness green nine implementation-guide.md -> … entries (first run)
Bold face green docs/getting-started.md selects FontName.HELVETICA_BOLD — injected with a matching decoration(BOLD), the form the first version of this guard accepted
API guidance green .github/ISSUE_TEMPLATE/feature_request.md offers BusinessTheme — injected by reverting #476's fix, then restored

The token list was built by checking each candidate rather than from memory: of ten, seven are absent from every src/main tree 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:

  • Extending DocumentationSnippetCompileTest to the root and module READMEs needs graph-compose-render-docx at test scope in qa (otherwise new DocxSemanticBackend() in render-docx/README.md fails to resolve and CI goes red for the wrong reason) plus doc-example markers across ten files.
  • The ShowcaseMetadata coverage 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.

PackageMapGuardTest completeness over document/backend/** — asserting every package with a package-info.java is named in CONTRIBUTING and the package map — would have caught the backend.fixed.pptx omission #476 fixed by hand. It is not implemented here.

DemchaAV added 2 commits July 31, 2026 19:46
…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.
@DemchaAV
DemchaAV merged commit c2899fe into develop Jul 31, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the test/doc-drift-guards branch July 31, 2026 19:19
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.

1 participant