Skip to content

Give every user-facing docs page its own search and social metadata - #508

Merged
hexsecs merged 3 commits into
mainfrom
claude/open-issues-review-i8j6sa
Sep 6, 2026
Merged

Give every user-facing docs page its own search and social metadata#508
hexsecs merged 3 commits into
mainfrom
claude/open-issues-review-i8j6sa

Conversation

@hexsecs

@hexsecs hexsecs commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Summary

mkdocs-material reuses site_description for any page without description: front matter, so all ~140 built pages shipped the identical meta description ("CLI-first CAN security research toolkit documentation"), and the sitemap presented mostly internal specs as the site's subject matter. Zero pages carried front matter before this change.

Closes #482

Changes

  • Unique meta descriptions on all 41 user-facing pages — getting started, the eight user-guide pages, all seven tutorials, all seventeen cookbook recipes, the developer workflow pages, and the agent pages. The cookbook and tutorial titles are the project's best long-tail search assets, so each recipe describes what it actually does ("Extract engine coolant temperature (SPN 110) samples from a J1939 capture with the SPN-aware decoder — no DBC file required").
  • Per-page social cards via Material's built-in social plugin, styled in the homepage's ink-and-caution-yellow palette. A shared docs URL now unfurls with that page's own title and summary. 143 cards render at build time.
  • Slimmer crawl surface. docs/design/, docs/tests/, and docs/benchmarks/ stay published and stay in the site's own search, but two theme overrides drop them from the sitemap (overrides/sitemap.xml) and mark them noindex, follow (overrides/main.html). The docs sitemap went from ~140 entries, mostly internal, to exactly the 41 user-facing pages.
  • CI and dependencies. The docs group takes mkdocs-material[imaging]; the docs workflow installs cairo and friends via apt and caches .cache/plugin/social, so the plugin's font download and rendered cards persist between runs.
  • A regression test (tests/test_docs_metadata.py) fails if a user-facing page is added without a description, exceeds 200 characters, or reuses another page's wording — and checks the two override files cover the same internal prefixes.

benchmarks/ is included in the internal set beyond the three items in the issue: it is the same kind of internal record, and it was the single remaining sitemap page still inheriting the site-wide description.

Test plan

  • uv run pytest -q — 1637 passed, 1 skipped, 293 subtests passed (44 new)
  • uv run ruff check and uv run ruff format --check — clean
  • bash scripts/build_pages_site.sh — builds clean under --strict
  • Verified in the built output: docs sitemap has 41 entries and 0 internal pages; a cookbook page carries its own description, og:description, and og:image; a design spec carries noindex, follow
  • Inspected a rendered social card (assets/images/social/cookbook/decode-spn-110.png) for correct title, description, and palette
  • uv sync --group docs --frozen resolves, and cairosvg/pillow import

Documentation

  • CHANGELOG.md updated under [Unreleased]
  • docs/command_spec.md updated (if the command surface changed) — no command surface change
  • docs/event-schema.md updated (if the structured-output shape changed) — no schema change
  • Touched docs/design/ spec updated (EARS syntax) — none touched
  • Touched docs/tests/ spec updated (Gherkin Given/When/Then) — none touched
  • AGENTS.md / docs/agents.md updated (if agent workflows changed) — no agent workflow change
  • mkdocs.yml nav updated (if any new docs pages were added) — no new pages

docs/docs_site.md gains "Page Metadata and Social Cards" and "Internal Pages" sections covering the front-matter requirement, the imaging dependencies, and the noindex/sitemap policy.

Safety

Not applicable — no active-bus behaviour is introduced or changed.

Note for the reviewer

One thing I could not verify locally: on a cold cache the social plugin downloads its font from Google Fonts, which is blocked from this sandbox. To check the cards actually render I converted a Roboto woff2 from npm into the plugin's font cache and built against that — the card image above is from a real build. On GitHub runners the download works normally, and the new cache step keeps later builds off the network. If a future air-gapped build is wanted, the fix is to vendor the font into .cache/plugin/social/fonts/Roboto/; I did not commit a font binary for that.

🤖 Generated with Claude Code

https://claude.ai/code/session_0195Fkk63xics9zBvjFgYz8T


Generated by Claude Code

…ixes #482)

`mkdocs-material` reuses `site_description` for any page without `description:`
front matter, so all ~140 built pages shipped the same meta description and the
sitemap presented mostly internal specs as the site's subject matter.

Add a unique description to all 41 user-facing pages: getting started, the user
guide, every tutorial, and every cookbook recipe. Enable the built-in `social`
plugin, in the homepage's ink-and-caution-yellow palette, so a shared docs URL
unfurls with that page's own title and summary rather than one site-wide
sentence.

Keep the design specs, test specs, and benchmark records published and
searchable on the site, but drop them from the sitemap and mark them
`noindex, follow` through theme overrides. The sitemap now lists 41 user-facing
pages instead of ~140.

Card rendering needs the `imaging` extra plus cairo and friends, so the docs
group and the docs workflow install them, and the workflow caches the plugin's
font and rendered cards between runs. A new test fails if a user-facing page is
added without a description or reuses another page's wording.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0195Fkk63xics9zBvjFgYz8T
Social-card rendering adds an image toolchain and a cold-cache font download to
the docs build, but the docs workflow only ran on pushes to main, so that build
was first exercised on the deploy path. Run the same build job on pull requests
and skip the artifact upload and deploy steps there.

The concurrency group becomes per-ref so a pull-request build cannot cancel a
deploy running for main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0195Fkk63xics9zBvjFgYz8T

hexsecs commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

unittest (python 3.13) is red on 459b131, and it is not this PR's failure.

What failed

FAILED tests/test_tui_app.py::test_capture_replacement_waits_for_previous_worker
  textual.css.query.NoMatches: No nodes match '#bus-status' on Screen(id='_default')

Why it is not this PR's

  • The diff touches docs markdown, mkdocs.yml, overrides/, the docs workflow, and two test files. It does not touch src/canarchy/tui_app.py or anything the TUI imports.
  • The only uv.lock additions are cairocffi, cairosvg, cssselect2, tinycss2, and webencodings — the imaging extra. No change to textual, rich, pytest, or any runtime dependency of the TUI.
  • unittest (python 3.12) passed in the same run, on the same commit.
  • Locally on Python 3.13 the file passes (16/16) and the named test passes 8/8 in isolation.

Root cause

on_mount starts set_interval(0.1, self._drain_capture). That timer keeps firing while the app tears down, after the widget tree is gone, so _refresh_bus_status reaches self.query_one("#bus-status", Static) with nothing to match. It is a genuine latent race in the TUI, exposed by timing rather than caused by it — the traceback shows the refresh running with capturing=True and a fully built lines list, failing only at the query.

Proposed patch

     def _drain_capture(self) -> None:
+        # The interval timer keeps firing while the app tears down, after the
+        # widget tree is gone; touching it then raises NoMatches from the
+        # refresh helpers. Nothing is left to draw at that point, so stop.
+        if not self.is_running:
+            return
         capture = self._capture
         if capture is None:
             return

Verified locally: TUI suites (test_tui_app.py, test_tui_capture.py, test_tui_snapshots.py) pass 59/59 on Python 3.13 across three consecutive runs with it applied. Depending on exactly when Textual clears is_running during teardown, the belt-and-braces version also makes the two Static refresh helpers tolerate a missing node.

I have not pushed this: it is production TUI code, unrelated to a docs-metadata change, and this repo tracks work through issues. I also could not re-run the failed job — the API returns 403 Resource not accessible by integration for rerun-failed-jobs.

Awaiting the maintainer's call on whether the fix rides along here or lands as its own change.


Generated by Claude Code

`unittest (python 3.13)` failed on this branch in
test_capture_replacement_waits_for_previous_worker, from a race in the TUI's
capture drain timer that this PR's diff does not touch. The fix is #510; it is
ported here so this PR can reach green without waiting on that one to merge,
and it no-ops once main carries it.

Original commit message follows.

---

`on_mount` starts a 0.1s interval that drains the capture queue and refreshes
the status line. Textual does not stop interval timers synchronously on exit,
so that drain could still run once the screen was unmounted, reaching
`query_one("#bus-status", Static)` with nothing to match and raising NoMatches
from a timer callback.

Skip the drain once the app is shutting down, and look the widgets up through
a helper that returns None when the node is absent, so the status line, the
J1939 ribbon, and the alert log survive the same window regardless of when
Textual clears `is_running`.

The regression test drives the refresh helpers after the `run_test` context has
exited; it raises NoMatches without this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0195Fkk63xics9zBvjFgYz8T

hexsecs commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Filed as #509 and fixed in #510. The same commit is ported here (195dd2c) so this PR can reach green without waiting on #510 to merge; it no-ops once main carries it.

The regression test raises NoMatches without the fix and passes with it, and the TUI suites are green on 3.12 and on 3.13 across three consecutive runs.


Generated by Claude Code

@hexsecs
hexsecs marked this pull request as ready for review September 6, 2026 00:28
@hexsecs
hexsecs merged commit dc2141f into main Sep 6, 2026
10 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 195dd2c20e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/canarchy/tui_app.py
Comment on lines +341 to +342
if not self.is_running:
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reference #509 in this commit

This commit implements the TUI teardown-race fix that its new test and changelog identify as issue #509, but the commit message references only #482. The repository's hard acceptance gate requires every implementing or fixing commit to reference its relevant issue with closes, fixes, or refs, so add refs #509 or split this fix into a properly referenced commit.

AGENTS.md reference: AGENTS.md:L32-L36

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SEO: per-page meta descriptions, social cards plugin, and slimmer crawl surface for the docs site

2 participants