From 536c27323f126124cb9c488312d40ea73bf98b27 Mon Sep 17 00:00:00 2001
From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
Date: Tue, 18 Aug 2026 21:45:15 +0200
Subject: [PATCH] fix(ci): the bot-serving monitor has been failing for ten
days
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Every scheduled run since 2026-08-09 failed, on one line: the check greps
for the literal `
anyplot.ai` and the home page now says
"anyplot.ai — AI-generated plot catalog for 15 libraries". Nothing else
was broken. Nobody looked.
That is the uncomfortable part. This run of SEO work edited this workflow
twice, and cited it repeatedly as the regression cover for exactly the
class of change that has no local verification loop — nginx behaviour,
bot serving. It was red the whole time. A monitor nobody reads protects
nothing, and the trailing-slash port leak that shipped today is the proof:
the guard was there and it was already failing.
The home check now matches on the prefix. That still distinguishes the
prerendered page from the SPA shell, whose title is "any.plot() — any
library.", which is the property under test; the full title was never the
point and made the check break on copy edits.
The trailing-slash case also no longer passes when there is no redirect
at all. An empty %{redirect_url} fell through to the success branch and
printed "OK: trailing slash -> " — so the rewrite disappearing entirely
would have read as a pass. Copilot raised this on two separate PRs and
neither addressed it.
Co-Authored-By: Claude Opus 5 (1M context)
---
.github/workflows/bot-serving-check.yml | 13 ++++++++++++-
CHANGELOG.md | 10 ++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/bot-serving-check.yml b/.github/workflows/bot-serving-check.yml
index 6b3e3e23a1..4eff940ee3 100644
--- a/.github/workflows/bot-serving-check.yml
+++ b/.github/workflows/bot-serving-check.yml
@@ -65,7 +65,12 @@ jobs:
}
# Bot path: prerendered per-route HTML from the seo-proxy
- check "$GOOGLEBOT" "$ORIGIN/" "anyplot.ai"
+ # Prefix, not the full title: the copy changed to "anyplot.ai —
+ # AI-generated plot catalog for 15 libraries" and this check went red
+ # for ten consecutive days without anyone noticing. The prefix still
+ # separates the prerendered page from the SPA shell, whose title is
+ # "any.plot() — any library.", which is the property under test.
+ check "$GOOGLEBOT" "$ORIGIN/" "anyplot.ai"
check "$GOOGLEBOT" "$ORIGIN/scatter-basic" "Basic Scatter Plot | anyplot.ai"
check "$TWITTERBOT" "$ORIGIN/scatter-basic/python/matplotlib" "Basic Scatter Plot - Matplotlib | anyplot.ai"
@@ -108,6 +113,12 @@ jobs:
slash_target=$(curl -sS --max-time 30 -o /dev/null -A "$GOOGLEBOT" \
-w '%{redirect_url}' "$ORIGIN/scatter-basic/")
case "$slash_target" in
+ "")
+ # No redirect at all: %{redirect_url} is empty, which the previous
+ # form printed as "OK: trailing slash -> " and passed. Copilot
+ # raised this twice; it means the rewrite has disappeared.
+ echo "::error::trailing slash produced no redirect — the rewrite is gone"
+ fail=1 ;;
*"/seo-proxy"*|http://*|*:8080/*)
echo "::error::trailing-slash redirect leaks or downgrades: $slash_target"
fail=1 ;;
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83c2fc8df5..2954449d23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -232,6 +232,16 @@ aggregate instead: an italic *Catalog* line at the end of the version section an
### Fixed
+- **The daily bot-serving monitor had been red for ten days** — it greps for an exact home-page
+ title, the copy changed to "anyplot.ai — AI-generated plot catalog for 15 libraries", and every
+ scheduled run since 2026-08-09 failed on that one line. Nothing else was wrong, and nobody looked:
+ this run of SEO work edited the workflow twice and cited it repeatedly as the regression cover for
+ changes that have no local verification loop, while it was failing daily. It now matches on the
+ prefix, which still separates the prerendered page from the SPA shell — the property actually
+ under test — and no longer breaks on copy. The trailing-slash check also no longer passes when
+ there is no redirect at all: an empty `%{redirect_url}` printed "OK" and returned success, which
+ Copilot raised on two separate PRs (#10478).
+
- **The trailing-slash redirect leaked the internal port** — #10473 removed a redirect to
`http://api.anyplot.ai/seo-proxy/…` and replaced it with `http://anyplot.ai:8080/…`: a smaller
version of the same defect, since nginx builds a `permanent` rewrite's Location from