Skip to content

docs: footer trademark notices scan different text than rehype-trademarks marks #497

Description

@EricAndrechek

Raised by CodeRabbit on #489 (docs/src/components/Footer.astro:51), and independently noticed-but-not-raised by the pre-push-reviewer gate on the same branch. Filing rather than folding in, because the fix is a design decision rather than a review nit.

The invariant, and how it breaks

Trademarks.astro states the contract in its own header:

The marks and their owners live in src/config/trademarks.ts, shared with src/plugins/rehype-trademarks.ts — the pass that puts the matching ® / ™ on the first mention in the prose above. One registry, so the footer can't promise a notice the prose didn't mark, or vice versa.

Both sides read one registry, but they scan different text:

  • rehype-trademarks.ts walks the rendered HTML and deliberately skips code, pre, kbd, samp, var, h1h6, .katex, .expressive-code, and the LinkButton/LinkCard components — each for a good reason (a ® inside a copy-pasteable command is wrong; marking a heading desyncs it from the TOC that was collected before the plugin runs).
  • Footer.astro builds its notice list from pageText(entry.data, entry.body ?? "") — the raw Markdown/MDX body, which contains all of those.

So a mark named only inside a heading or a fenced block produces a footer notice with no matching symbol anywhere in the prose. Live example: docs/src/content/docs/architecture.md mentions OpenTelemetry only in excluded structures, and still emits an OpenTelemetry notice.

The direction is the safe one — over-attributing is better than claiming a mark the page never names — which is why it isn't blocking. But it is precisely the mismatch the component says can't happen.

Options

  1. Have the plugin publish the marks it actually matched (per page) and let the footer render notices from that set. Exact by construction, and the invariant becomes structural rather than a comment. Costs a build-time channel from the rehype pass to the footer component.
  2. Teach pageText() the same exclusions — strip fenced/inline code and ATX/setext headings from the body before scanning. Cheaper, no plumbing, but it duplicates the eligibility rules in a second place and they will drift.
  3. Weaken the documented invariant to "the footer may name a mark the prose didn't symbol-mark, never the reverse," and say why. Zero code, but gives up a property worth having.

Option 1 is the one that actually holds the line; option 2 is the pragmatic version if the plumbing isn't worth it.

Also in scope while there

Trademarks.astro derives from entry.body, so a mark appearing only inside a code fence still yields a footer notice — the same root cause, and it would be fixed by the same change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions