Skip to content

feat: share cards, crawler routes, and an AEO brief - #8

Merged
msitarzewski merged 1 commit into
mainfrom
feat/discoverability
Aug 27, 2026
Merged

msitarzewski merged 1 commit into
mainfrom
feat/discoverability

Conversation

@msitarzewski

Copy link
Copy Markdown
Owner

What

The board had no description, no share card, and nothing for crawlers. A link to it rendered as a bare URL, and search or answer engines had to infer what the tool does from a JavaScript-rendered page.

Head metadata

Description, canonical, OG and Twitter card metadata with absolute image URLs, theme-color for both schemes, icons, and JSON-LD (WebSite + WebApplication) stating what FinTick does and how.

The JSON-LD deliberately claims no more than the tool does — it describes an application that aggregates, extracts, and corroborates. No NewsArticle, no Dataset, nothing implying FinTick is a news publisher or a market-data provider.

Routes

Route Notes
/robots.txt Points at the sitemap. Disallows /api/ (no prose to index) and ?ops — the same page plus operator telemetry, which would read as duplicate content. The canonical link covers engines that ignore the query rule.
/sitemap.xml Single URL, changefreq: hourly.
/llms.txt Plain-language brief for answer engines.
/og.png, /favicon.svg, /apple-touch-icon.png Served from an allowlist so the handler never resolves a caller-supplied path. Day-long cache; the board itself stays no-store.

llms.txt states the method and the limits: what each status means, that aggregation is the dedup, that every post is preserved by URI, that social posts are stream signal and never corroboration — and that FinTick reports whether an event has been corroborated, not that it is true, and is not investment advice. Answer engines reward a stated method and explicit limits over marketing copy, and it is the honest framing regardless.

Bug fixed: HEAD returned 501

Social scrapers HEAD an og:image before fetching it. BaseHTTPRequestHandler answers 501 for any verb without a do_ method, which reads to them as a broken image. do_HEAD now routes through do_GET and drops the body, keeping every header including Content-Length.

Assets

The share card is generated at 1200×630 in the board's own visual language — brand, lede, ticker strip, a breaking card showing 0 external sources · ahead of the wire, and the status pills. The icon is an F with the wordmark's detached caret, rasterized deterministically so it has no build dependency.

pyproject.toml gains package-data; without it the assets are dropped from a built distribution and /og.png 404s. Production runs from a git checkout so this is latent, not live.

Testing

Seven new tests covering every route, asset content types and caching, that share metadata is present and absolute (a relative og:image is the classic silent break — scrapers do not resolve it), that the JSON-LD parses, that the sitemap is well-formed against the real namespace, and that HEAD returns headers with an empty body.

The HTTP harness moved into a ServedBoardFixture that carries no tests of its own — DiscoverabilityTests previously subclassed DashboardHttpTests and re-ran its entire suite.

Verified against a live server: all 10 routes return expected status, content type, and cache headers; no console errors (the CSP admits the favicon); 145 passed locally, up from 138, with the same 13 pre-existing macOS-only failures before and after.

🤖 Generated with Claude Code

The board had no description, no share card, and nothing for crawlers — a
link to it rendered as a bare URL and search or answer engines had to infer
what the tool does from a JavaScript-rendered page.

Adds to the head: description, canonical, OG and Twitter card metadata with
absolute image URLs, theme-color for both schemes, icons, and JSON-LD
describing the site and the application.

Adds four routes:
- /robots.txt points at the sitemap, and disallows /api/ (no prose to index)
  and ?ops, which is the same page plus operator telemetry and would read as
  duplicate content. The canonical link covers engines that ignore the query
  rule.
- /sitemap.xml
- /llms.txt, a plain-language brief for answer engines: what each status
  means, how aggregation and post accounting actually work, and the explicit
  limits — corroboration is not truth, and none of it is investment advice.
- the share card, favicon, and touch icon, served from an allowlist so the
  handler never resolves a caller-supplied path, with a day-long cache while
  the board itself stays no-store.

Also implements do_HEAD. Social scrapers HEAD an og:image before fetching it
and the base handler answers 501 for any verb without a do_ method, which
reads to them as a broken image.

The share card is generated at 1200x630 in the board's own visual language.
The icon is a rasterized F with the wordmark's detached caret.

Tests cover every route, that share metadata is present and absolute (a
relative og:image is the classic silent break — scrapers do not resolve it),
that the JSON-LD parses, and that HEAD returns headers with no body. The HTTP
server harness moved into a fixture that carries no tests of its own, so the
two suites no longer re-run each other's cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@msitarzewski
msitarzewski merged commit 545ab01 into main Aug 27, 2026
12 checks passed
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.

1 participant