feat(examples): hold the committed previews to the code that renders them - #487
Merged
Conversation
…them README and the showcase site read files under assets/readme rather than rendering anything, and nothing held those files to the catalogue. A change to an example, a theme or the engine moved the render while the committed file stayed where it was, and the first anybody knew was a release publishing it — which is how a deck went two releases without the bold weights its styles asked for. Twenty-three of the sixty-seven previews were behind the code at the point this was written; they are re-rendered here. All twenty-two of the PDFs among them rasterise to the same pixels as before, so nothing visible was carrying the drift, and nothing visible is buried by refreshing them. Every preview is now compared against a fresh render on each build. The comparison is exact and drops only what a machine writes rather than an author: a PDF's clock-seeded identifier, a package's zip and creation stamps, the platform's line separator after an XML declaration, the box a freeform's path is normalised against, and one named watermark whose antialiasing differs between machines. Each was measured by rendering the catalogue on both platforms at one version rather than argued from — 99 of 104 documents came out byte-identical under it — so the gate needs no tolerance, and every exemption is a line somebody had to write. A committed preview is rendered at the released version while a working tree is already on the next one, so the version the previews carry is recorded next to them and the module's tests render at it. Without that the coordinate pill alone would differ and the comparison would fail on every preview that has one. The README hero is checked too. It is not part of the catalogue — the release script renders it alone — which is exactly why it drifts unnoticed between releases. Editing a preview by hand now runs the job that checks it.
The runner rejected the README hero and nothing else: all sixty-seven previews matched across the two platforms, and the hero did not, because it is not a document — it is a page rasterised to a PNG, and rasterising text is exactly where two machines disagree. Comparing it would take a pixel budget, which is a number nobody can defend a year later. It is also not the only one: eight rasterised figures sit beside the preview folder, all of them the same kind of file for the same reason. They are written down and asserted as a set, so a ninth arrives as a decision rather than as a commit nobody read. The figures of the 1.5 line are left out of that entirely — re-rendering those would be the bug.
…what it did The hero test took a raster comparison down with it when the runner showed rasters cannot be compared, and two pieces of it stayed: a branch reading a PNG by its pixels, which no caller reaches and which says the opposite of the list beside it, and a comment justifying itself by a caller that is gone. The guard over the assets it cannot compare listed files and passed over folders, which are easier to add without noticing, not harder. Both are asserted now. The CHANGELOG said all twenty-three refreshed previews rasterise to the same pixels. Twenty-two of them do; the twenty-third is a DOCX, which nothing rasterised, and whose change was real — it asked for a font named Helvetica-Bold, which Word honours only if that font is installed, and now marks bold.
…wapped image Comparing the files that are there means a deleted preview leaves its own guard: README loses a figure, the folder shrinks, and every test stays green. The catalogue is now pinned against the folder plus a list of the documents deliberately left unpublished, so the three have to account for each other exactly — a deletion, an example nobody decided to publish, and a rename that lands as one of each all fail. The list is the shorter half of the decision, and it replaces the deck-only lists, which were the same idea applied to six files out of sixty-seven. The exemption for the showcase watermark exempted the path rather than the renders. Any image of the same size passed: a different word, a swapped logo, a blank. It now names the pixel digest of each machine's version, and only those two collapse onto one token; anything else keeps its own digest. Two tests hold that shut — an unknown image of exactly the right size is not absorbed, and the part actually inside both decks has to be one of the two renders the fixtures account for, since naming a path protects nothing once the deck stops carrying what was measured. The creation stamp was dropped by matching any ISO instant anywhere under docProps, which would have taken a date somebody meant along with it. It names dcterms:created in docProps/core.xml, which is the one element that was measured varying, and a custom date property now proves it survives. The property recording the previews' version said the release script moves it. No release step does yet, and until one exists a release re-renders the showcase at the new version while the previews stay behind — with this gate comparing both sides at the old one and staying green. Both notes say so.
Reading each freeform's path in slide coordinates dropped the box those coordinates were measured against — and a box is not bookkeeping. It is the centre a rot turns a shape around and the axis a flipH mirrors it in, and five of the six committed decks carry one or the other. Two shapes with the same path and the same angle in differently sized boxes turn about different points and draw differently; the comparison called them one shape. The scale it applied came from the first path in a shape and was used for every point in it, which is wrong the moment a shape has two paths with different extents. Comparing all six decks shape by shape against a render from the runner found the difference in exactly two shapes, both in one deck. Those two are named now, with the digest of each machine's version, the way the unstable watermark already was — so the exemption covers a pair of renders rather than a kind of shape, and every other freeform is compared as it was written. The preview folder was listed for its files, with folders stepped over in silence: a preview one level down was in none of the sets these tests compare. The folder is asserted flat, which is how the site reads it.
This was referenced Aug 2, 2026
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.
Second of the two changes for #461. The first made the catalogue render one way, from an empty tree; this one holds the committed previews to it.
Why
README and the showcase site read files under
assets/readme/**rather than rendering anything, and nothing held those files to the catalogue. A change to an example, a theme or the engine moved the render while the committed file stayed where it was, and the first anybody knew was a release publishing it — which is howmaven-banner.pptxwent two releases without the bold weights its styles asked for, found by hand in #486 rather than by a gate.Twenty-three of the sixty-seven previews were behind the code, and they are re-rendered here. All twenty-two PDFs among them rasterise to the same pixels as before at 100 DPI — the drift was in the operator stream, not in what a reader sees — so nothing visible was carrying it and nothing visible is buried by refreshing them. The one DOCX is the exception worth naming: it expressed bold by asking for a font called
Helvetica-Bold, which Word honours only if that font is installed, and now marks<w:b/>instead. All 56 runs keep the weight they had.What
The comparison is exact. The original sketch for this gate was rasterisation at a fixed DPI with a pixel budget; the measurement says that is not needed and would be worse. Rendering the catalogue on Windows and on the runner at one version puts 99 of 104 documents byte-identical once the machine is left out of it, and the five that are not are each accounted for:
twin-output.pptxword-export-companion.docxdocPropsmaster-showcase.pptxengine-deck.pptxemoji-svg-vs-png.pdfSo
AssetContentdrops a PDF's clock-seeded/ID, a package's zip timestamps,dcterms:createdindocProps/core.xml, and the platform's line separator after an XML declaration — and compares everything else, images included, pixel for pixel.Every exemption names one thing and the two renders it covers it between. The watermark is exempted by the pixel digest of each machine's version, not by its path: an image of the same size the fixtures do not account for keeps its own digest and fails. The same shape holds for geometry. Comparing all six decks shape by shape found the box difference in exactly two freeforms, both in
twin-output.pptx, so those two are named by digest and every other shape is compared as written — a box is the centre arotturns a shape around and the axis aflipHmirrors it in, and five of the six decks carry one or the other. A tolerance is what nobody can justify a year later; a named exemption with a measurement behind it is.The version the previews carry is recorded. A committed preview is rendered at the released version while a working tree is already on the next one, so the two are different documents before anything else is weighed.
graphcompose.examples.assetVersioninexamples/pom.xmlrecords it and surefire pins the display version to it, so the module's tests render what the previews are.Nothing moves that property yet, and this gate cannot notice. It compares both sides at whatever the property says, so a release that re-renders
web/showcaseat the new version while leaving the previews behind passes here. That is the release step's job — and the reason this should land next to it rather than long before: merged alone and left, the next cut puts the previews a version behind with nothing going red.What cannot be compared is written down. Eight rasterised figures sit beside the preview folder, the README hero among them. Each is a page rasterised to a PNG, and rasterising text is where the two machines disagree — the first run of this gate proved it, rejecting the hero and nothing else while all sixty-seven previews matched. Comparing them would take a pixel budget, so instead the eight are listed and asserted as a set: a ninth arrives as a decision rather than as a commit nobody read.
assets/readme/v1.5is left out entirely — those are a released line's figures and re-rendering them would be the bug.assets/readme/**joins thecodepath filter, so hand-editing or hand-reverting a preview runs the job that checks it.The PPTX comparison from #486 moves into
AssetContentwith its tests rather than being duplicated for PDF and DOCX; every test it had is still here, plus one for the/ID, one for the creation stamp, and one for line endings.Out of scope
cut-release.ps1re-renders the hero at every tag, so no release publishes one that is behind.cut-release.ps1— the release-side asset step, the-SkipShowcasesplit and the staging allow-list are the next change.ShowcaseMetadataCoverageTestrecords a deliberate decision not to assert it, since the fallback card is the point of having one; overturning that needs its own justification and an explicit list of documents left to the fallback, not a line in a PR about drift.Tests
examples71 tests andqa688, green locally. The reactor is green on JDK 17, 21 and 25; the asset-drift gate itself runs in Examples Generation Smoke Test, which is the baseline JDK 17 alone.The gate was also run against a deliberately swapped preview: it fails naming that file alone.