From ba4128ac4e69fb38de5f8a1952e30c75d2dbc944 Mon Sep 17 00:00:00 2001 From: bagowix Date: Thu, 13 Aug 2026 13:57:04 +0400 Subject: [PATCH 1/5] docs: make the package and docs landing pages describe the project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two metadata defects on the pages that are linked and ranked the most. `Homepage` and `Repository` in `[project.urls]` both pointed at the GitHub repository, so the PyPI sidebar rendered two identically-targeted links and offered no route to the documentation. `Homepage` now points at the docs site. Zensical never populates `page.is_homepage`, so the theme's title logic fell through to the "page title - site name" branch and the documentation landing page came out as `interlock - interlock` — the brand twice, and not one word saying what the project does. Override the `htmltitle` block for the homepage only; every other page keeps the theme default. --- CHANGELOG.md | 17 +++++++++++++++++ overrides/main.html | 12 ++++++++++++ pyproject.toml | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e889c6..d23c1e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,23 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed + +- **The PyPI sidebar no longer links to the same page twice.** `Homepage` and + `Repository` both pointed at the GitHub repository, so a reader arriving on + the package page got two identically-targeted links and no obvious route to + the documentation. `Homepage` now points at the documentation site; the + repository stays reachable through `Repository`. + +### Fixed + +- **The documentation landing page was titled `interlock - interlock`.** + Zensical does not populate `page.is_homepage`, so the theme fell through to + the generic "page title - site name" branch and duplicated the project name + on the one page that is linked and ranked the most, leaving it without a + single word describing what the project is. The landing page now carries a + descriptive title; every other page is unchanged. + ## [2.6.0] - 2026-08-12 ### Added diff --git a/overrides/main.html b/overrides/main.html index e0a1aa4..04316a7 100644 --- a/overrides/main.html +++ b/overrides/main.html @@ -1,5 +1,17 @@ {% extends "base.html" %} +{# Zensical does not populate page.is_homepage, so the theme falls through to + "{{ page.title }} - {{ site_name }}" and the landing page ends up titled + "interlock - interlock" — a duplicated brand and no keyword for the one page + search engines rank first. The homepage is identified by its empty url. #} +{% block htmltitle %} + {% if not page.url %} + interlock — a modern circuit breaker for Python + {% else %} + {{ super() }} + {% endif %} +{% endblock %} + {% block scripts %} {{ super() }} diff --git a/pyproject.toml b/pyproject.toml index 20cde5b..db0e062 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ requests = ["requests>=2.31.0"] aiohttp = ["aiohttp>=3.12.0"] [project.urls] -Homepage = "https://github.com/bagowix/interlock" +Homepage = "https://bagowix.github.io/interlock/" Documentation = "https://bagowix.github.io/interlock/" Repository = "https://github.com/bagowix/interlock" Changelog = "https://github.com/bagowix/interlock/blob/main/CHANGELOG.md" From 12beeff1ff264256617f360af17070e36851f7f1 Mon Sep 17 00:00:00 2001 From: bagowix Date: Thu, 13 Aug 2026 14:20:13 +0400 Subject: [PATCH 2/5] docs: title every page for readers who have not arrived yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Page titles were written for someone already inside the site — "Comparison", "httpx", "Timeout" — which tells a reader meeting the project in a search result or a pasted link nothing about what they are looking at. The landing page was worse: Zensical never populates `page.is_homepage`, so the theme fell through to " - " and rendered "interlock - interlock". Add a `seo_titles` map to `zensical.toml`, keyed by page url and consumed by the `htmltitle` block, covering all 25 published pages. Keeping the titles as data in the config rather than as branches in the template means adding a page is a one-line change; a page left out keeps the theme default. Also emit Open Graph and Twitter card tags, which the theme does not provide at all, so a link to the docs stops rendering as a bare url when it is shared. The card is the text-only `summary` kind — Zensical ships no social-card generation and the docs carry no image asset to point `og:image` at. --- CHANGELOG.md | 19 +++++++++++++++++-- overrides/main.html | 32 ++++++++++++++++++++++++++------ zensical.toml | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d23c1e3..6f697e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + +- **Documentation pages now carry Open Graph and Twitter card tags.** A link to + any page pasted into Slack, Reddit or a chat rendered as a bare url, because + the theme emits no such tags and nothing supplied them. Each page now declares + its title, description and canonical url. The card is the text-only `summary` + kind: Zensical ships no social-card generation and the documentation carries + no image asset. + ### Changed - **The PyPI sidebar no longer links to the same page twice.** `Homepage` and @@ -14,14 +23,20 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). the documentation. `Homepage` now points at the documentation site; the repository stays reachable through `Repository`. +- **Page titles now describe the page to someone who has not arrived yet.** + Titles were written for a reader already inside the site — `Comparison`, + `httpx`, `Timeout` — which tells someone meeting the project in a search + result or a pasted link nothing about what they are looking at. A + `seo_titles` map in `zensical.toml` gives each page a self-describing title; + a page left out of the map keeps the theme default. + ### Fixed - **The documentation landing page was titled `interlock - interlock`.** Zensical does not populate `page.is_homepage`, so the theme fell through to the generic "page title - site name" branch and duplicated the project name on the one page that is linked and ranked the most, leaving it without a - single word describing what the project is. The landing page now carries a - descriptive title; every other page is unchanged. + single word describing what the project is. ## [2.6.0] - 2026-08-12 diff --git a/overrides/main.html b/overrides/main.html index 04316a7..753d810 100644 --- a/overrides/main.html +++ b/overrides/main.html @@ -1,17 +1,37 @@ {% extends "base.html" %} -{# Zensical does not populate page.is_homepage, so the theme falls through to - "{{ page.title }} - {{ site_name }}" and the landing page ends up titled - "interlock - interlock" — a duplicated brand and no keyword for the one page - search engines rank first. The homepage is identified by its empty url. #} +{# Page titles are written for someone already inside the site — "Comparison", + "httpx", "Timeout" — which says nothing to someone meeting the project in a + search result or a pasted link. `seo_titles` in zensical.toml maps a page url + to the title that page should carry outside the site. It also covers the + landing page: Zensical never populates `page.is_homepage`, so the theme falls + through to "{{ page.title }} - {{ site_name }}" and titles it + "interlock - interlock". A page absent from the map keeps the theme default. #} +{% set seo_titles = config.extra.seo_titles | d({}) %} + {% block htmltitle %} - {% if not page.url %} - interlock — a modern circuit breaker for Python + {% if page.url in seo_titles %} + {{ seo_titles[page.url] }} · {{ config.site_name }} {% else %} {{ super() }} {% endif %} {% endblock %} +{# Without these a link to the docs pasted into Slack, Reddit or a chat renders + as a bare url. Zensical ships no social-card generation and the docs carry no + image asset, so this is the text-only `summary` card, not a large one. #} +{% block extrahead %} + {% set og_title = seo_titles[page.url] if page.url in seo_titles else (page.title | striptags) %} + + + + + + + + +{% endblock %} + {% block scripts %} {{ super() }} diff --git a/zensical.toml b/zensical.toml index 7be33b5..23b7085 100644 --- a/zensical.toml +++ b/zensical.toml @@ -67,3 +67,35 @@ custom_fences = [ alternate_style = true [project.markdown_extensions.pymdownx.tasklist] custom_checkbox = true + +# Titles for readers who have not arrived yet: search results, pasted links, +# social cards. Keyed by page url ("" is the landing page), consumed by the +# htmltitle and extrahead blocks in overrides/main.html, which append the site +# name. Keep each under ~60 characters or search engines truncate it. A page +# left out of this map keeps the theme's " - interlock". +[project.extra.seo_titles] +"" = "A modern circuit breaker for Python" +"getting-started/" = "Getting started with a Python circuit breaker" +"comparison/" = "Python circuit breaker libraries compared" +"migration/" = "Migrating from pybreaker or circuitbreaker" +"demo/" = "A runnable Python circuit breaker demo" +"correctness/" = "How the circuit breaker is tested for correctness" +"reference/" = "Python circuit breaker API reference" +"guides/configuration/" = "Configuring a Python circuit breaker" +"guides/states/" = "Circuit breaker states and manual control" +"guides/failure-classification/" = "Which failures should trip a circuit breaker" +"guides/observability/" = "Circuit breaker metrics and OpenTelemetry" +"guides/timeout/" = "Timeouts with a Python circuit breaker" +"guides/retries/" = "Retries and circuit breakers in Python" +"guides/pipeline/" = "Resilience pipeline: timeout, retry, bulkhead" +"integrations/" = "Circuit breaker integrations for Python clients" +"integrations/fastapi/" = "FastAPI circuit breaker with 503 and Retry-After" +"integrations/litestar/" = "Litestar circuit breaker with 503 and Retry-After" +"integrations/httpx/" = "httpx circuit breaker transport" +"integrations/httpx2/" = "httpx2 circuit breaker transport" +"integrations/aiohttp/" = "aiohttp circuit breaker middleware" +"integrations/requests/" = "requests circuit breaker adapter" +"integrations/redis/" = "Distributed circuit breaker state in Redis" +"integrations/tenacity/" = "tenacity retries with a circuit breaker" +"integrations/llm/" = "Circuit breaker for OpenAI and Anthropic calls" +"integrations/frameworks/" = "Circuit breaker for Flask and Django" From 5e32b60d45a9268745a3a65f38353f64ef15bc07 Mon Sep 17 00:00:00 2001 From: bagowix Date: Thu, 13 Aug 2026 14:25:50 +0400 Subject: [PATCH 3/5] docs: verify the documentation site with Google Search Console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the ownership meta tag for the URL-prefix property https://bagowix.github.io/interlock/. The token is public by design — it ships in the HTML of every page — and proves ownership only, granting no access. No changelog entry: this changes nothing a user of the library can observe. Emitted on every page rather than the landing page alone. Google reads it from the property root only, but the unconditional form costs nothing and avoids depending on landing-page detection, which Zensical gets wrong. --- overrides/main.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/overrides/main.html b/overrides/main.html index 753d810..4c9c81b 100644 --- a/overrides/main.html +++ b/overrides/main.html @@ -21,6 +21,12 @@ as a bare url. Zensical ships no social-card generation and the docs carry no image asset, so this is the text-only `summary` card, not a large one. #} {% block extrahead %} + {# Proves ownership of https://bagowix.github.io/interlock/ to Google Search + Console. Public by design, not a secret. Google only reads it from the + property root, but emitting it everywhere costs nothing and does not depend + on detecting the landing page — the very thing Zensical gets wrong above. + Removing this tag silently un-verifies the property. #} + {% set og_title = seo_titles[page.url] if page.url in seo_titles else (page.title | striptags) %} From dd4fdae1183087bb5ee46f37a94b34a2579741c4 Mon Sep 17 00:00:00 2001 From: bagowix Date: Thu, 13 Aug 2026 19:41:43 +0400 Subject: [PATCH 4/5] fix: drop the Documentation url that now duplicates Homepage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointing `Homepage` at the documentation site relocated the duplicate rather than removing it: `Homepage` and `Documentation` became the same url, so the PyPI sidebar still rendered two identically-targeted links — the exact defect the change set out to fix, and the changelog claimed it was gone. `Homepage` already reaches the documentation, so `Documentation` carries nothing. Removing it leaves four links to four distinct destinations. --- CHANGELOG.md | 11 ++++++----- pyproject.toml | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f697e1..9050057 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Changed -- **The PyPI sidebar no longer links to the same page twice.** `Homepage` and - `Repository` both pointed at the GitHub repository, so a reader arriving on - the package page got two identically-targeted links and no obvious route to - the documentation. `Homepage` now points at the documentation site; the - repository stays reachable through `Repository`. +- **Every link in the PyPI sidebar now goes somewhere different.** `Homepage` + and `Repository` both pointed at the GitHub repository, so a reader arriving + on the package page got two identically-targeted links and no obvious route + to the documentation. `Homepage` now points at the documentation site and the + redundant `Documentation` entry is gone; the repository stays reachable + through `Repository`. - **Page titles now describe the page to someone who has not arrived yet.** Titles were written for a reader already inside the site — `Comparison`, diff --git a/pyproject.toml b/pyproject.toml index db0e062..e886830 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,6 @@ aiohttp = ["aiohttp>=3.12.0"] [project.urls] Homepage = "https://bagowix.github.io/interlock/" -Documentation = "https://bagowix.github.io/interlock/" Repository = "https://github.com/bagowix/interlock" Changelog = "https://github.com/bagowix/interlock/blob/main/CHANGELOG.md" Issues = "https://github.com/bagowix/interlock/issues" From cf02b339d5f17057332e9d487db8424a37fec6a3 Mon Sep 17 00:00:00 2001 From: bagowix Date: Thu, 13 Aug 2026 19:50:24 +0400 Subject: [PATCH 5/5] docs: record the Search Console verification in the changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5e32b60 argued the tag needed no changelog entry because no user of the library can observe it. That reasoning does not survive its own precedent: the same [Unreleased] section already records the Open Graph tags and the page titles, which are equally invisible from Python. The changelog covers the project, not only the importable surface, and AGENTS.md grants no exemption. Records what the tag buys — visibility into how the documentation is indexed — and the trap that removing it silently un-verifies the property. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9050057..3254956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). kind: Zensical ships no social-card generation and the documentation carries no image asset. +- **The documentation site is verified with Google Search Console.** Every + generated page carries the ownership tag for the property, so the maintainers + can finally see which pages are indexed and which searches reach them — + previously a blind spot. Nothing about the library itself changes. Removing + the tag silently un-verifies the property. + ### Changed - **Every link in the PyPI sidebar now goes somewhere different.** `Homepage`