ci(guards): run every guard the job names, and compile docs-only pull requests - #474
Merged
Conversation
… 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.
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
The
Architecture and Documentation Guardsjob 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 in9340df10, 2026-07-01) andTemplateComposeApiTest(deleted in47241c83, 2026-05-06) — resolve to nothing. Two others,PdfRenderInterfaceGuardTestandDocumentationExamplesTest, live ingraph-compose-render-pdfandgraph-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: 27andBUILD 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.
codematched**/*.java, poms, resources andmvnw, but nothing.md, andbuild-and-testgates oncode. A pull request touching only documentation therefore skipped the reactor build and merged withoutDocumentationSnippetCompileTest— the guard whose whole purpose is compiling the java fences published indocs/— 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.PackageMapGuardTestis new to the list — it is core-resident, scansREADME.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-Dtestpattern is applied to every module in the closure and each non-matching one hard-fails, so widening-plwould require-Dsurefire.failIfNoSpecifiedTests=falseand reinstate the silent skip. They run inbuild-and-test, which now also covers documentation changes.CiGuardListGuardTestparsesci.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 undercore/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,#methodsuffix, 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 newdocsfilter matches**/*.md, andbuild-and-testgates oncode || docs. It is deliberately absent fromjvm, so a documentation change runs the reactor on JDK 17 alone rather than the three-JDK matrix, andexamples-generationstill gates oncodealone — installing eight modules to render 94 documents proves nothing about prose.PackageMapGuardTestno longer scans gitignoreddocs/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.CanonicalSurfaceGuardTestalready excludes it for the same reason — the two are now consistent.Three false comments.
ci.ymlcalledrender-docx,graph-compose-testingandrender-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.ymlcalledrender-pptx"the semantic PPTX backend"; its primary backend is fixed-layout, alongside an older semantic manifest skeleton.codeql.ymlclaimed 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.xmldeclaresorg.apache.pdfbox:pdfbox. The module importsPDFont,PDType1Font,Standard14FontsandRandomAccessReadBufferwhile declaring onlyfontbox, taking the rest through mediation.pdfbox.versionis3.0.8in bothrender-pptx/pom.xml:63andrender-pdf/pom.xml:67, so the resolved version does not move.Verification
Full reactor gate, unpiped exit code read from Maven itself:
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:javadocacross the six published modules exits 0.The rewritten guard command runs six classes (30 tests) where it ran four:
CiGuardListGuardTestwas exercised in both directions by injecting a name intoci.ymland re-running it:GhostGuardThatDoesNotExistcom.demcha.documentation.GhostFqTestcom.demcha.documentation.PackageMapGuardTestGhostMethodTest#someCasePackageMapGuardTest#oldPackageNamesShouldNotReturn…com/demcha/documentation/PackageMapGuardTest*GuardTest**/documentation/*TestGhostPrefix*TestCiGuardListGuardTes?CiGuardListGuardTes?X!SomeRetiredTestThat last group is the second commit. Resolving wildcards instead of accepting them surfaced a defect in the parser itself: the character class reading the
-Dtestvalue omitted?, soCiGuardListGuardTes?Xwas truncated toCiGuardListGuardTesand 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/:PackageMapGuardTestfailed withExpecting empty but was: ["docs/private/skill-audit-2026-07-10.md"].The
docsfilter itself is evaluated by GitHub and is not reproducible locally; the parsed workflow confirmsbuild-and-testgates oncode || docsandexamples-generationoncodealone. This pull request touchesci.yml, whichcodealready 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 onmainbyte-for-byte, so it shipped in 2.1.0; it reachesmainthrough the normal release merge rather than a separate hotfix.