From 3a034356a237341452b7afebd3a2bfd21021609f Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Wed, 19 Aug 2026 12:05:41 -0500 Subject: [PATCH] ci: run the Python lane for docs and website script changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit llms.txt coverage is asserted in Python, but website/ sat on the Python skip list, so a PR adding a docs page — or regressing the generator — went green without ever running the test that checks the page is reachable. That is how the index drifted to 53% coverage unnoticed. --- scripts/ci/classify_changes.py | 14 +++++++++++++- tests/ci/test_classify_changes.py | 19 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/scripts/ci/classify_changes.py b/scripts/ci/classify_changes.py index 3a3e268423..db79da5c7b 100644 --- a/scripts/ci/classify_changes.py +++ b/scripts/ci/classify_changes.py @@ -45,6 +45,9 @@ must never skip one a change could break: * ``website/static/oauth/`` is python-relevant too: it publishes the OAuth Client ID Metadata Document that ``tests/tools/test_mcp_cimd.py`` checks against the pinned callback ports in ``tools/mcp_oauth.py``. +* ``website/docs/`` and ``website/scripts/`` are python-relevant for the same + reason: the docs tree generates ``llms.txt``, and + ``tests/website/test_generate_llms_txt.py`` asserts every page reaches it. """ from __future__ import annotations @@ -66,7 +69,16 @@ _PY_SKIP = ("docs/", "website/") + _FRONTEND # callback ports in tools/mcp_oauth.py, so editing it alone must still run the # Python lane — otherwise dropping a redirect URI goes green here and breaks # every CIMD login on main. -_PY_RELEVANT_SITE = ("website/static/oauth/",) +# website/docs/ and website/scripts/ are asserted about the same way. The docs +# tree generates llms.txt — the index every LLM (Hermes included, via the +# hermes-agent skill) reads to learn what Hermes can do — and +# tests/website/test_generate_llms_txt.py holds every page to appearing in it. +# Skipping Python on a docs-only PR is how the index drifted to 53% coverage. +_PY_RELEVANT_SITE = ( + "website/static/oauth/", + "website/docs/", + "website/scripts/", +) # CI-sensitive files: eslint config, workflow files, composite actions. # Changes here can influence what code the autofix job executes and pushes to diff --git a/tests/ci/test_classify_changes.py b/tests/ci/test_classify_changes.py index 3a33f576f7..56fe3a7ad5 100644 --- a/tests/ci/test_classify_changes.py +++ b/tests/ci/test_classify_changes.py @@ -76,11 +76,15 @@ CASES = { # Lockfile bump shifts every TS package's tree, but not the Python suite. "root lockfile → frontend, not python": (["package-lock.json"], _lanes(frontend=True, npm_lock=True)), "nested lockfile → npm_lock": (["website/package-lock.json"], _lanes(site=True, npm_lock=True)), - "website → site": (["website/docs/intro.md"], _lanes(site=True)), + # A website file the Python suite cannot read stays site-only. + "website config → site": (["website/docusaurus.config.ts"], _lanes(site=True)), # uv lock --check re-resolves against PyPI, so it must stay off for any # diff that can't desync the lockfile — a registry blip on a docs PR # otherwise shows up as a blocking "uv.lock out of sync" red X. - "docs → no uv_lock": (["website/docs/user-guide/profiles.md"], _lanes(site=True)), + "docs → no uv_lock": ( + ["website/docs/developer-guide/plugins/index.md"], + _lanes(python=True, site=True), + ), "frontend → no uv_lock": (["apps/desktop/src/store/profile.ts"], _lanes(frontend=True)), # The published CIMD document is asserted about by the Python suite, so a # lone edit there must not skip the lane that would catch a bad edit. @@ -88,6 +92,17 @@ CASES = { ["website/static/oauth/client-metadata.json"], _lanes(python=True, site=True), ), + # A new docs page must reach llms.txt, and the generator that puts it there + # has its own tests. Skipping Python on either is how the index drifted to + # 53% coverage while every PR stayed green. + "docs page → python + site": ( + ["website/docs/user-guide/bot-mode.md"], + _lanes(python=True, site=True), + ), + "docs generator → python + site": ( + ["website/scripts/generate-llms-txt.py"], + _lanes(python=True, scan=True, site=True), + ), # SKILL.md reads like docs, but the skill-doc tests read skills/, so a # skill edit must still run Python. "skill md → python + site": (["skills/github/SKILL.md"], _lanes(python=True, site=True)),