Add pinned out-of-date banner and /latest/ canonicals for versioned docs - #437
Merged
miharp merged 3 commits intoAug 6, 2026
Merged
Conversation
Two follow-ups to the multi-version infrastructure, part of OpenVoxProject#320: Banner: every page of a frozen (non-latest) doc version gets a notice pinned to the bottom of the viewport linking to the product's /latest/ docs. Coverage is derived from _data/products.yml, so nothing renders today; the moment a product gains a new version and its 'latest' key moves, all pages of the older collections show the banner automatically. Like the version picker, the banners persist outside the Turbo content frame and are re-synced on each frame load. Canonicals: a small Jekyll hook points each numbered-latest page (e.g. /openvox/8.x/...) at its byte-identical /latest/ twin via page.canonical_url, which the theme's head.html already honors, so search engines index the stable /latest/ URLs instead of splitting ranking across the duplicate pair. Frozen versions keep their default self-canonical since their content is unique once 'latest' moves on. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Michael Harp <mike@mikeharp.com>
The banner previously fired for any version that wasn't `latest`, which is wrong during a Phase 1 major-version cutover (MAINTAINING.md): a new version can sit ahead of `latest` in the versions list (newest first) as a beta preview, deliberately not yet promoted. That version is not "no longer actively updated" — it's the opposite. Track whether the loop has passed the `latest` entry and only emit the banner for versions after it, matching the documented newest-first ordering in _data/products.yml. Verified: a Liquid-only render against synthetic 9x/8x(latest)/7x data confirmed 9x renders no banner, 8x renders no banner, 7x renders a visible one. Also verified live against a merged-in OpenVox 9 cutover branch (docs/openvox9-cutover) via jekyll serve + Playwright screenshots: no banner on /openvox/9.x/ or /openvox/8.x/, and a temporary synthetic older version confirmed the banner still renders correctly when it should. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Michael Harp <mike@mikeharp.com>
miharp
marked this pull request as ready for review
August 4, 2026 13:14
miharp
marked this pull request as draft
August 4, 2026 14:06
version-banner.html's out-of-date logic (and the version selector) both rely on an unenforced convention: `versions` listed newest-first, `latest` naming a real entry. Nothing caught a violation before this would silently mis-derive version state. Add rake test:products_data, checking latest resolves, ids are unique, and ids sort newest-first (by leading version-id number), and wire it into the Rake Checks CI job alongside rubocop. Verified locally: passes on current products.yml and on the real OpenVox 9 cutover branch's data (docs/openvox9-cutover), and correctly fails on injected bad-ordering, duplicate-id, and unknown-latest cases. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Michael Harp <mike@mikeharp.com>
miharp
force-pushed
the
docs/outdated-version-banner
branch
from
August 4, 2026 14:07
088b741 to
0918ade
Compare
miharp
marked this pull request as ready for review
August 4, 2026 14:11
Contributor
|
I suspect that you started by looking for an existing plugin for the |
Contributor
Author
|
Off-the-shelf canonical plugins only emit self-referential URLs or pass through front matter; the part that needed code is computing the numbered-to- |
binford2k
approved these changes
Aug 5, 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.
Part of #320.
Addresses @binford2k's concern in #320 that bookmarks and search results will keep pointing at old versions once a product cuts a new major.
Pinned out-of-date banner
Every page of a frozen (non-latest) doc version now shows a notice pinned to the bottom of the viewport, linking to the product's
/latest/docs. Coverage is derived from_data/products.yml: nothing renders today (every numbered version is still its product's latest), but the moment a product gains a new version and itslatestkey moves, all pages of the older collections show the banner automatically — no per-page front matter. Like the version picker, the banners live outside the Turbo content frame and are re-synced on each frame load.The banner links to the product's
/latest/root rather than the same page under/latest/, because we have no redirect mechanism and a same-page link would 404 whenever a page was renamed or removed in the newer version. Once the 404 page grows a "search for this page" affordance (tracked in #438), we can revisit a same-page link.Update: the original version of this logic banner-ed any version that wasn't
latest, which is wrong during a Phase 1 major-version cutover (seeMAINTAINING.md) — a new version can sit ahead oflatestin_data/products.ymlas a beta preview, deliberately not yet promoted (this is exactly the shape #430's OpenVox 9 cutover takes). That preview version is not "no longer actively updated," so it must not get this banner. Fixed by tracking whether the loop has passed thelatestentry in the (documented newest-first)versionslist and only emitting the banner after that point.Verified three ways:
9x/8x(latest) /7xdata:9xand8xrender no banner,7xrenders one.docs/openvox9-cutoverbranch (not part of this PR) viajekyll serve: no banner on/openvox/9.x/or/openvox/8.x/.latest.Screenshots below: OpenVox 9.x (preview, no banner), OpenVox 8.x (latest, no banner), and a synthetic older version (banner shown, for comparison).
CI check for
products.ymlconsistencyThe banner logic above (and the version selector) both depend on an unenforced convention:
versionslisted newest-first, andlatestnaming a real entry in that list. Nothing previously caught a violation of either — a misordered list or a typo'dlatestwould silently mis-derive every version's state. Addedrake test:products_data, wired into theRake ChecksCI job, which validates:latestresolves to a real version id, version ids are unique, and ids sort newest-first (by leading version number). Verified it passes against both the currentproducts.ymland the realdocs/openvox9-cutoverdata, and correctly fails against injected bad-ordering, duplicate-id, and unknown-latestcases.Canonical URLs for the numbered/latest duplicate pair
While
latestpoints at a numbered version, that collection publishes byte-identical content under two URLs (e.g./openvox/8.x/...and/openvox/latest/...). A small Jekyll hook (_plugins/canonical_latest.rb) now setspage.canonical_urlon the numbered copy pointing at its/latest/twin; the theme'shead.htmlalready honors that key, so search engines index the stable/latest/URLs instead of splitting ranking across the pair. Frozen versions keep their default self-canonical, since their content is unique oncelatestmoves on.Note for #401:
jekyll-seo-tagalso honorspage.canonical_url, so this composes with the SEO-tags work rather than conflicting with it.