From 1aae98a6d34996be5222f864adde0ccf3d9dacb3 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Fri, 7 Aug 2026 22:00:35 -0700 Subject: [PATCH 1/5] Restore gallery captions and items the conversion flattened (#55) * Restore gallery captions and items the conversion flattened Closes #52. A gallery is a flex row whose column widths come from `.gallery-cols-N figure`, so anything inside it that is not a figure gets no width from that rule and is laid out as one more item in the row. Three variants of that survived the conversion, and no gate could see any of them: every media check reasons about a URL, and misplaced content resolves and is linked exactly as correct content is. Each was verified against the captured live site before being changed rather than inferred from the markup, and the first is why that matters. Eleven captions in the FLAIR post were written as text trailing the last figure shortcode. Reading only the source, the obvious fix is to move the text into that figure's caption parameter, which is what the review suggested. The capture shows all eleven were blocks-gallery-caption elements, captions for the set, so that fix would have attributed a caption describing four images to whichever one sorted last and would have looked right. They move to a caption on the gallery instead. The other two were gallery items the conversion emitted as plain markdown, two bare images and eight linked ones, against li.blocks-gallery-item > figure in the original. Both become figure shortcodes, the linked ones through the parameter the theme already has, which puts the anchor back inside the figure where it was. The container becomes a figure, because figcaption is only valid as a figure's child. The reset already sets figure { margin: 0 } and the theme already styles figure > figcaption, so a set caption needed one rule: a full-width flex basis, without which it packs onto the end of the last row and reads as a caption for whichever image it lands beside. check-url-parity.py gains the gate that would have caught all of this, parsing the built pages rather than matching text, since nested figures defeat a regex. Demonstrated failing before being trusted: against the pre-fix content it reports 27 stray nodes and exits 1, and it distinguishes the shapes rather than counting them, naming bare text and each stray element type. The gate found more than the review did. The linked-image variant is on a post neither the review nor the issue mentioned, and it was found only by running the check across all 19 pages that carry a gallery. Verified through make-release.sh and against the local mirror: PASS - 1245 URLs honored, with assets at 1049 and orphans at 98, both unmoved, so no media was lost or newly orphaned by the rewrite. Co-Authored-By: Claude Opus 5 (1M context) * Reject a gallery page by the class token, not by one spelling of it Both from review on #55. The cheap reject read `class="gallery`, which assumes the quotes survive and that the token sorts first. Minification drops the quotes around a value that does not need one and says nothing about class order, so `class=gallery` and `class="foo gallery"` both skipped the parser entirely and the gate passed vacuously on a page whose markup was merely spelled differently. It matches the bare word now, which cannot skip a page the parser would find, since the parser requires the class token and a page carrying it always contains the string. Matching a page that only mentions the word costs one parse. The printed count is now taken from what the parser found rather than from the reject, so the broader test does not turn "pages carrying a gallery" into "pages the word appears on". The shortcode comment claimed a set caption needed no rule of its own, while this branch adds one. It does need it, and for a reason worth keeping: the gallery is a flex container, so without a full-width basis the caption packs onto the end of the last row and reads as a caption for whichever image it lands beside. The comment now says so and says not to remove the rule as redundant. Verified on all three spellings, quoted, unquoted, and reordered, each carrying a known stray node: all three are now detected, and the first two were not before. The site itself is unchanged at 19 pages with galleries and 0 stray nodes. Co-Authored-By: Claude Opus 5 (1M context) * Name the invariant the gallery check tests, not the shapes it has met From a suppressed finding on #55, which was right: the docstring said "both conversion artifacts" while the README and the description say three. It was written after two were known and never revised when the check itself found the third. Rewritten to say what it actually tests. Three source patterns reached it and they arrive as three different HTML shapes, so an enumeration is a list to extend every time the conversion surprises us again. The invariant is that a gallery holds figures and its own caption, and that is what covers the shape nobody has thought of yet, which is how the third was found after the first two. Co-Authored-By: Claude Opus 5 (1M context) * Record why the gallery scanner has no handle_startendtag From a suppressed finding on #55, which reasoned correctly from the code and reached the wrong conclusion about the base class. HTMLParser forwards a self-closing tag to handle_starttag and then handle_endtag, so `
`, `
` and `` are already reported as stray children and already leave the depth balanced. Verified on those three spellings and on a self-closing non-void `
`, which enters and exits correctly and lets the `

` after it be seen. Adding the override the finding asks for is what would break this, by counting a pair the base class already splits. That is worth a comment rather than a reply nobody reading the file later will find. Co-Authored-By: Claude Opus 5 (1M context) * Say what a gallery failure found, rather than calling it missing From a suppressed finding on #55. The shared summary printed `FAIL gallery: N missing` for a check whose findings are nodes that are present, which is the entire complaint about them. Each label can now name its own noun, defaulting to "missing" so the four older checks are unchanged and a check added later reads the way they do unless it says otherwise. Verified by forcing a failure: `FAIL gallery: 15 stray nodes`. Co-Authored-By: Claude Opus 5 (1M context) --------- Co-authored-by: Claude Opus 5 (1M context) --- assets/css/extended/custom.css | 10 ++ checks/README.md | 18 +++ checks/check-url-parity.py | 120 ++++++++++++++++-- ...-a-supermicro-bpn-sas3-846el1-backplane.md | 4 +- .../06/21/moving-from-unraid-to-proxmox-ve.md | 16 +-- ...ents-to-keep-room-temperatures-balanced.md | 44 +++---- layouts/shortcodes/gallery.html | 19 ++- 7 files changed, 189 insertions(+), 42 deletions(-) diff --git a/assets/css/extended/custom.css b/assets/css/extended/custom.css index 4fe3fe5..c7c0eff 100644 --- a/assets/css/extended/custom.css +++ b/assets/css/extended/custom.css @@ -38,6 +38,16 @@ body { margin: 1rem auto; } +/* + The set caption is a flex item like the figures beside it, so without a full-width basis it + packs onto the end of the last row and reads as a caption for whichever image it lands next + to. Full width puts it on its own row under the set, which is what it describes. +*/ +.gallery > figcaption { + flex: 0 0 100%; + text-align: center; +} + .gallery-cols-1 figure { width: 100%; } diff --git a/checks/README.md b/checks/README.md index 435916f..83bc682 100644 --- a/checks/README.md +++ b/checks/README.md @@ -84,6 +84,24 @@ A count is all the check can observe, and two causes reach each direction: it ri **Both directions read absolute references as well as relative ones.** Hugo writes an absolute URL wherever a template resolves one against the base, which the entry-cover image on every list page does. Reading only rooted paths made those files look linked from nowhere while they were being displayed, and left a broken one unchecked in the other direction. The origin is read from the home page's canonical link rather than assumed, since staging and production build with different base URLs and a hardcoded host would check one environment's output against another's. No canonical link is a hard failure, because a guessed origin inflates the orphan count by exactly the pages that use one. +## The gallery check, which no direction above can reach + +Every check above reasons about a URL: whether it renders, whether it resolves, whether anything points at it. Content misplaced **inside** a gallery satisfies all of that. The file exists, the reference resolves, and something links it, so the media surface is green in both directions while the page is laid out wrong. The defect is one of structure, which is why three variants of it survived the conversion and every gate since. + +A gallery is a flex row whose column widths come from `.gallery-cols-N figure`. Anything in there that is not a `figure` gets no width from that rule and is rendered as one more item in the row. So the check reads the built pages and fails on any direct child of a gallery container that is not a `figure` or the gallery's own `figcaption`. + +The three shapes it found, all of them conversion artifacts, and each verified against the captured live site before being changed: + +| Shape in the markdown | What the old platform had | +| --- | --- | +| Caption text appended after the last `figure` shortcode's `}}` | `