From b2d79a349ce80e74e193f8b56a0ac0041dbff1ed Mon Sep 17 00:00:00 2001 From: Mohsen Seyedkazemi Ardebili Date: Fri, 2 Oct 2026 23:28:50 +0200 Subject: [PATCH 1/6] fix(docs): normalize versioned site URL joins --- docs/overrides/main.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/overrides/main.html b/docs/overrides/main.html index 77baf01..e77d624 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -17,6 +17,7 @@ {% block extrahead %} {%- set page_title = (page.meta and page.meta.title) or page.title or config.site_name -%} {%- set page_desc = (page.meta and page.meta.description) or config.site_description -%} + {%- set site_base = config.site_url.rstrip('/') ~ '/' -%} @@ -26,7 +27,7 @@ - + {%- if page.meta and page.meta.keywords %} @@ -44,7 +45,7 @@ - + ', html, re.DOTALL ) @@ -181,12 +211,11 @@ def check_page(self, rel: str) -> None: def _breadcrumb_target_exists(self, url: str) -> bool: parsed = urlparse(url) - if parsed.scheme != "https" or parsed.netloc != "mskazemi.com": + if parsed.scheme != self.base_scheme or parsed.netloc != self.base_netloc: return False - prefix = "/aobench/" - if not parsed.path.startswith(prefix): + if not parsed.path.startswith(self.base_path): return False - suffix = parsed.path[len(prefix) :].lstrip("/") + suffix = parsed.path[len(self.base_path) :].lstrip("/") if not suffix: return (self.site / "index.html").is_file() target = self.site / suffix @@ -297,6 +326,11 @@ def check_sitewide_structured_data(self) -> None: def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--site-dir", type=Path, default=ROOT / "site") + parser.add_argument( + "--base-url", + default="https://mskazemi.com/aobench/", + help="Expected public base URL for this build; normalized to one trailing slash.", + ) args = parser.parse_args() if not args.site_dir.is_dir(): @@ -306,10 +340,11 @@ def main() -> int: ) return 1 - checker = Checker(args.site_dir) + checker = Checker(args.site_dir, args.base_url) checker.check_robots() checker.check_sitemap() checker.check_llms_txt() + checker.check_template_leaks() for page in KEY_PAGES: checker.check_page(page) checker.check_sitewide_structured_data() From fb4784980a21b26e93788c604e7ab7e9771a8a44 Mon Sep 17 00:00:00 2001 From: Mohsen Seyedkazemi Ardebili Date: Fri, 2 Oct 2026 23:29:45 +0200 Subject: [PATCH 4/6] test(docs): audit slash-safe URLs sitewide --- scripts/seo_check.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/scripts/seo_check.py b/scripts/seo_check.py index c07dc5a..2b16d77 100644 --- a/scripts/seo_check.py +++ b/scripts/seo_check.py @@ -181,14 +181,6 @@ def check_page(self, rel: str) -> None: f"{rel}: meta description", ) - for suffix, label in ( - ("llms.txt", "llms.txt alternate uses slash-safe base"), - ("assets/social-preview.png", "Twitter image uses slash-safe base"), - ("about/changelog/", "announcement link uses slash-safe base"), - ): - expected = self.base_url + suffix - self.check(expected in html, f"{rel}: {label}", expected) - blocks = re.findall( r'', html, re.DOTALL ) @@ -315,6 +307,21 @@ def check_structured_data(self, rel: str, html: str) -> None: f"{rel}: Dataset distributions declare URL and encodingFormat", ) + def check_sitewide_base_urls(self) -> None: + pages = sorted(self.site.rglob("index.html")) + self.check(bool(pages), "slash-safe URL audit found built pages") + expected_refs = ( + ("llms.txt", "llms.txt alternate uses slash-safe base"), + ("assets/social-preview.png", "Twitter image uses slash-safe base"), + ("about/changelog/", "announcement link uses slash-safe base"), + ) + for path in pages: + rel = path.relative_to(self.site).as_posix() + html = path.read_text(encoding="utf-8") + for suffix, label in expected_refs: + expected = self.base_url + suffix + self.check(expected in html, f"{rel}: {label}", expected) + def check_sitewide_structured_data(self) -> None: pages = sorted(self.site.rglob("index.html")) self.check(bool(pages), "structured-data audit found built pages") @@ -347,6 +354,7 @@ def main() -> int: checker.check_template_leaks() for page in KEY_PAGES: checker.check_page(page) + checker.check_sitewide_base_urls() checker.check_sitewide_structured_data() if checker.failures: From af15aaedbebe3690a66e83f81822b51ed24cd75b Mon Sep 17 00:00:00 2001 From: Mohsen Seyedkazemi Ardebili Date: Fri, 2 Oct 2026 23:29:47 +0200 Subject: [PATCH 5/6] ci(docs): test mike-style site URL --- .github/workflows/docs-integrity.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/docs-integrity.yml b/.github/workflows/docs-integrity.yml index cc228ac..9c0d80a 100644 --- a/.github/workflows/docs-integrity.yml +++ b/.github/workflows/docs-integrity.yml @@ -66,6 +66,18 @@ jobs: - name: Discoverability surfaces are published run: uv run python scripts/seo_check.py + - name: Mike-style versioned site_url is slash-safe + # mike rewrites site_url to a version path without a trailing slash. Reproduce + # that exact input so concatenation bugs cannot hide behind the normal config. + run: | + cp mkdocs.yml .mkdocs-versioned.yml + trap 'rm -f .mkdocs-versioned.yml; rm -rf site-versioned' EXIT + sed -i 's|^site_url:.*|site_url: https://mskazemi.com/aobench/latest|' .mkdocs-versioned.yml + uv run mkdocs build --strict -f .mkdocs-versioned.yml --site-dir site-versioned + uv run python scripts/seo_check.py \ + --site-dir site-versioned \ + --base-url https://mskazemi.com/aobench/latest/ + - name: Examples still run run: uv run python -m pytest tests/test_examples.py -q From b3d76adf87184a2dfb7a4848219e4d858f1f4af4 Mon Sep 17 00:00:00 2001 From: Mohsen Seyedkazemi Ardebili Date: Fri, 2 Oct 2026 23:29:50 +0200 Subject: [PATCH 6/6] docs(changelog): record issue 29 fix --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 608c47f..e5dd607 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## Unreleased +### Fixed — versioned documentation URLs no longer lose the path separator + +- Normalize mike's slashless versioned `site_url` before appending paths in the theme + override. The `llms.txt` alternate, Twitter preview image, changelog banner link, and + breadcrumb ancestor URLs now resolve under `/aobench/latest/` instead of malformed + paths such as `/aobench/latestllms.txt`. +- Exclude `docs/overrides/**` from published documentation so raw Jinja source is not + exposed as a public page. +- Extend the SEO gate with a no-trailing-slash versioned build and site-wide assertions + for these URLs, including breadcrumb resolution and the override-source leak. + ### Fixed — structured data now matches Google's breadcrumb and Dataset requirements - Breadcrumb JSON-LD now emits only real navigable ancestors, gives every emitted