Skip to content

ci(guards): run every guard the job names, and compile docs-only pull requests - #474

Merged
DemchaAV merged 2 commits into
developfrom
ci/guard-scope-and-docs-gate
Jul 31, 2026
Merged

ci(guards): run every guard the job names, and compile docs-only pull requests#474
DemchaAV merged 2 commits into
developfrom
ci/guard-scope-and-docs-gate

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Why

The Architecture and Documentation Guards job named eight test classes and ran four of them.

It selects by name with -Dtest= while scoping the reactor to -pl :graph-compose-core. Two of the names — CanonicalTemplateComposerPdfBoundaryTest (deleted in 9340df10, 2026-07-01) and TemplateComposeApiTest (deleted in 47241c83, 2026-05-06) — resolve to nothing. Two others, PdfRenderInterfaceGuardTest and DocumentationExamplesTest, live in graph-compose-render-pdf and graph-compose-qa, which that module scope cannot reach.

Surefire fails a run only when the entire selection is empty; a name that matches nothing while its siblings match is dropped without a warning. Running the job's exact command reports Tests run: 27 and BUILD SUCCESS — four classes, no mention of the other four. The -Dtest= line was last edited after both dead classes had already been removed, so a human read that line and did not catch it.

Separately, markdown was not a change-detection input. code matched **/*.java, poms, resources and mvnw, but nothing .md, and build-and-test gates on code. A pull request touching only documentation therefore skipped the reactor build and merged without DocumentationSnippetCompileTest — the guard whose whole purpose is compiling the java fences published in docs/ — ever running.

What changed

ci.yml, guard job. The -Dtest= list is now the six guards that actually live in the engine module: EnginePdfBoundaryTest, DocumentationCoverageTest, CanonicalSurfaceGuardTest, PackageMapGuardTest, VersionConsistencyGuardTest, CiGuardListGuardTest. PackageMapGuardTest is new to the list — it is core-resident, scans README.md / CONTRIBUTING.md / docs/ for retired package names, and cost nothing to include. The two cross-module guards are not re-added here: with -am, a -Dtest pattern is applied to every module in the closure and each non-matching one hard-fails, so widening -pl would require -Dsurefire.failIfNoSpecifiedTests=false and reinstate the silent skip. They run in build-and-test, which now also covers documentation changes.

CiGuardListGuardTest parses ci.yml, extracts the -Dtest= value from every step whose reactor scope is exactly :graph-compose-core, and asserts each name selects at least one file under core/src/test/java. It is itself in that list, so it runs inside the job it protects and a bad edit to that line fails the step that made it. Every selector form is resolved against the tree — plain name, package-qualified name, #method suffix, and glob alike: the selector is translated to a regex (** spans packages, * and ? stay inside one segment) and matched against both the simple name and the package path of each test source. A wildcard matching nothing is the same silent skip as a deleted name, so it is checked rather than waved through. A guard-the-guard assertion fails if no core-scoped selection is found at all, so the check cannot pass vacuously after the workflow is restructured.

ci.yml, change detection. A new docs filter matches **/*.md, and build-and-test gates on code || docs. It is deliberately absent from jvm, so a documentation change runs the reactor on JDK 17 alone rather than the three-JDK matrix, and examples-generation still gates on code alone — installing eight modules to render 94 documents proves nothing about prose.

PackageMapGuardTest no longer scans gitignored docs/private/. A local planning note there routinely names a retired package in order to record that it is retired, which reads as a violation; because the directory never reaches the remote, a hit fails the release runbook's §0.B local verify gate for a reason CI can neither reproduce nor clear. CanonicalSurfaceGuardTest already excludes it for the same reason — the two are now consistent.

Three false comments. ci.yml called render-docx, graph-compose-testing and render-pptx "not on Central"; all three are published, and the real reason they are installed locally is that the examples pin them to ${graphcompose.version}, the reactor's -SNAPSHOT. publish.yml called render-pptx "the semantic PPTX backend"; its primary backend is fixed-layout, alongside an older semantic manifest skeleton. codeql.yml claimed its scope "matches the canonical verify gate"; it carries no -am, so only core is built and the published render backends ship unscanned. Widening it is left for 2.2, when the standing alert set can be triaged in the same pass.

render-pptx/pom.xml declares org.apache.pdfbox:pdfbox. The module imports PDFont, PDType1Font, Standard14Fonts and RandomAccessReadBuffer while declaring only fontbox, taking the rest through mediation. pdfbox.version is 3.0.8 in both render-pptx/pom.xml:63 and render-pdf/pom.xml:67, so the resolved version does not move.

Verification

Full reactor gate, unpiped exit code read from Maven itself:

./mvnw -B -ntp clean verify -pl :graph-compose-core,:graph-compose-render-pdf,:graph-compose-render-docx,:graph-compose-render-pptx,:graph-compose-templates,:graph-compose-testing,:graph-compose,:graph-compose-bundle,:graph-compose-qa,:graph-compose-coverage -am

BUILD SUCCESS, 13/13 modules, 1:58. Core 412 tests (411 before, +1 for the new guard), qa 681, templates 104, render-pdf 139, testing 90, render-docx 18, render-pptx 5 — zero failures, zero errors. ./mvnw javadoc:javadoc across the six published modules exits 0.

The rewritten guard command runs six classes (30 tests) where it ran four:

./mvnw -B -ntp clean "-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest" test -pl :graph-compose-core

CiGuardListGuardTest was exercised in both directions by injecting a name into ci.yml and re-running it:

Injected selector Expected Result
(none — the real list) pass exit 0
GhostGuardThatDoesNotExist fail exit 1
com.demcha.documentation.GhostFqTest fail exit 1
com.demcha.documentation.PackageMapGuardTest pass exit 0
GhostMethodTest#someCase fail exit 1
PackageMapGuardTest#oldPackageNamesShouldNotReturn… pass exit 0
com/demcha/documentation/PackageMapGuardTest pass exit 0
*GuardTest pass exit 0
**/documentation/*Test pass exit 0
GhostPrefix*Test fail exit 1
CiGuardListGuardTes? pass exit 0
CiGuardListGuardTes?X fail exit 1
!SomeRetiredTest pass (exclusion, skipped) exit 0

That last group is the second commit. Resolving wildcards instead of accepting them surfaced a defect in the parser itself: the character class reading the -Dtest value omitted ?, so CiGuardListGuardTes?X was truncated to CiGuardListGuardTes and the guard checked a name nobody wrote. ?, /, ! and + are now admitted.

Before this change the local reactor gate could not pass at all on a tree with notes under docs/private/: PackageMapGuardTest failed with Expecting empty but was: ["docs/private/skill-audit-2026-07-10.md"].

The docs filter itself is evaluated by GitHub and is not reproducible locally; the parsed workflow confirms build-and-test gates on code || docs and examples-generation on code alone. This pull request touches ci.yml, which code already matched, so it runs the full gate either way — the first documentation-only pull request after merge is what exercises the new path.

Not in scope

The same broken -Dtest= list is on main byte-for-byte, so it shipped in 2.1.0; it reaches main through the normal release merge rather than a separate hotfix.

DemchaAV added 2 commits July 31, 2026 03:56
… requests

The "Architecture and Documentation Guards" job selected eight test classes
while scoping the reactor to graph-compose-core. Two had been deleted months
earlier; two live in graph-compose-qa and graph-compose-render-pdf. Surefire
aborts only when a selection is entirely empty, so the four names that did
match kept the job green while it ran half of what it advertised.

The list is now the five guards that live in the engine module, and
CiGuardListGuardTest parses the workflow and fails the job when a name in it
stops resolving under core/src/test/java — inside the very job it protects.

Markdown was also missing from change detection, so a pull request touching
only .md skipped the reactor build entirely and merged without
DocumentationSnippetCompileTest ever compiling the java fences it publishes.
A separate `docs` filter routes those changes into the reactor slice that
carries the qa guards, on the baseline JDK; example generation stays gated on
`code`, since rendering the catalogue proves nothing about prose.

PackageMapGuardTest scanned gitignored docs/private/, where a local note
routinely names a retired package in order to record that it is retired. A hit
there fails the release runbook's local verify gate for a reason CI can never
reproduce, so the directory is excluded — as CanonicalSurfaceGuardTest already
excludes it.

Three workflow comments claimed things that are not true: that render-docx,
render-pptx and graph-compose-testing are absent from Maven Central (they are
published; the examples pin them to the reactor's -SNAPSHOT), that the pptx
module is a semantic backend (its primary backend is fixed-layout), and that
the CodeQL scope matches the canonical verify gate (it is narrower, and the
render backends ship unscanned).

graph-compose-render-pptx compiles against org.apache.pdfbox types while
declaring only fontbox; the dependency is now explicit. Both modules pin
pdfbox.version to 3.0.8, so the resolved version is unchanged.
…olve wildcards

CiGuardListGuardTest asserted that every name in the workflow's -Dtest list
resolves under core/src/test/java, and its own Javadoc claimed it "runs inside
the very job it protects". It was not in that list. It ran only in the full
reactor build, so a phantom name added to the guard step would still have
passed the guard step. Adding the test to the list makes the claim true and
costs nothing — it is core-resident like the other five.

Wildcard selectors were accepted without resolving them. A pattern that matches
nothing is the same silent skip as a deleted class name: Surefire drops it as
long as a sibling matches. Every accepted form is now resolved against the tree
— plain name, package-qualified name, #method suffix and glob alike — by
translating the selector to a regex (** spans packages, * and ? stay inside one
segment) and matching it against both the simple name and the package path of
each test source.

Exercising that surfaced a defect in the parser itself: the character class
reading the -Dtest value omitted '?', so a selector containing one was silently
truncated and the guard then checked a name nobody wrote. '?', '/', '!' and '+'
are now admitted, and a leading '!' marks an exclusion — it subtracts from the
selection rather than claiming coverage, so it is skipped rather than required
to resolve.
@DemchaAV
DemchaAV merged commit 832994e into develop Jul 31, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the ci/guard-scope-and-docs-gate branch July 31, 2026 07:14
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