A real report, written by 70 · SEO & Discoverability, run against this repo — dogfood output, committed unedited. This is the artifact every brief ends in: findings that cite their evidence, ranked by severity, with a fix sketch each.
Date: 2026-07-09 Auditor: SEO & discoverability brief (70), read-only pass Prior run: none — no previous SEO.md existed; this is the first SEO audit. Method note: live fetches of goal-prompts.vercel.app were not possible from this environment (outbound blocked), so this audit was run against the built artifacts in the repo — index.html, b/, p/, examples/, sitemap.xml, robots.txt, raw/, sw.js — which are regenerated by build.py and match HEAD (clean tree), i.e. they are byte-for-byte what Vercel deploys. Routing behavior (cleanUrls, redirects, headers) was read from vercel.json.
Indexable and shareable today: yes. This is a fully static site with real SEO machinery, actually used: 180 URLs in sitemap.xml, a correct robots.txt, unique per-page <title> + meta description + canonical on every brief and playbook page (emitted by page() in build.py:758–787), per-brief 1200×630 OG images (og/*.png, 141 files, verified dimensions), no noindex anywhere, one h1 per page, no query-param duplicates (all catalog state lives in the URL hash), and cleanUrls: true in vercel.json collapsing .html variants. The 141 b/ pages are exemplary: full content in static HTML including the verbatim brief text, dense static cross-links ("pairs well with", family siblings, playbook links).
The single biggest leak: the homepage's catalog — the site's whole reason to rank — does not exist in its static HTML. The 141 brief cards and 35 playbook cards are rendered client-side from a ~507 KB inline const DATA blob (index.html:765, render code around index.html:966–1052). Outside <script> tags, index.html contains exactly one link to a brief page (/b/46) and one to a playbook (/p/codereview-collab). Non-rendering crawlers (most non-Google bots, LLM/AI-answer crawlers, social scrapers) see a homepage with no path to 176 detail pages, and even for Google the homepage passes almost no static link equity to them — discovery routes entirely through the sitemap. The same blob makes the homepage a 572 KB HTML document (~165 KB gzipped), the site's only real Core Web Vitals liability.
| Page | What the crawler sees | What the unfurl renders | Gap |
|---|---|---|---|
/ (index.html) | Full head (unique title, description, canonical, SoftwareApplication JSON-LD at line 20), hero h1, section headings and prose — but the catalog grid at #catalog (line 577) is empty markup filled by JS; 1 static brief link, 1 static playbook link; 572 KB document | Rich card: og.png 1200×630, og:title/description, summary_large_image — correct | The product (141 briefs) is invisible without JS; near-zero static internal linking; heaviest page on the site |
/b/47 (b/47.html) | Complete static article: breadcrumb, h1 "The Fixer", lede, phases, the full verbatim brief in a <pre>, related-brief and playbook links — all crawlable without JS; ~10 KB gzipped | Per-brief card: og/47.png 1200×630, unique og:title/description, canonical https://goal-prompts.vercel.app/b/47 | Only polish: no JSON-LD (the visible breadcrumb has no BreadcrumbList), heading skip h2→h4 |
/examples/ (examples/index.html) | Static list of real sample reports with title + meta description — fine for indexing; it is in the sitemap | Nothing — the page has no og:* tags, no twitter:card, and no <link rel="canonical"> (grep confirms zero matches) | A shared link to the site's best proof asset unfurls as a bare URL |
index.html (grid built by JS from const DATA at line 765; static HTML outside scripts links to only /b/46 and /p/codereview-collab); source: template.htmltemplate.html at build time — a plain <ul>/card list of all 141 brief links (id · name · one-liner) grouped by family, which the existing JS then enhances or replaces on load; or a static <noscript> index; or a build-generated HTML sitemap page linked from the footerindex.html (572,093 bytes total; 506,961 bytes inside <script>, dominated by all 141 full brief bodies in const DATA; ~165 KB gzipped vs ~10 KB for a b/ page)body from the inline DATA and fetch raw/<id>.md (already deployed, network-only in sw.js) or a slimmed catalog.json on demand at copy time; keep offline copies via the service worker cache. Pairs naturally with SEO-1: static cards replace inline JSONbuild.py:1106 passes {BASE}/og.png for every playbook page; confirmed in p/day1.html, studio.html, vitals.html (all og:image = /og.png)og/NN.pngscripts/og.py to render per-playbook cards (name, brief count, family color bars from build.FAMILY_COLORS) into og/p-<key>.png and wire them through playbook_detail/examples/ has no Open Graph tags and no canonicalexamples/index.html head (has title + description only; zero og:, twitter:, or canonical matches) — it is a sitemap URL (sitemap.xml line 6)build.py's page() helper)studio.html and vitals.html lack rel="canonical"studio.html, vitals.html heads (both have full og/meta but no canonical; the generated pages all have one via build.py:774)cleanUrls: true in vercel.json 308-redirects .html variants, so real duplication risk is small; consistency and self-canonicalization are still worth having<link rel="canonical" href="https://goal-prompts.vercel.app/studio"> (and /vitals) to the two source pagesindex.html:20, SoftwareApplication); b/.html and p/.html have none (grep: 0 matches) despite visible breadcrumbs on every b/ pagebrief_detail/playbook_detail in build.py — all the data (title, description, dates, family) is already in hand at build time<lastmod>sitemap.xml (180 <url> entries, <loc> only), generated at build.py:1389–1397<lastmod> per URL from each source file's git commit date (or build date as a floor) in the sitemap writervercel.json (outputDirectory: ".") serves everything: raw/*.md (verbatim duplicates of each b/ page's brief text), prompts/**/*.md (a third copy), root reports (BUGS.md, FUNNEL.md, …, linked from b/ pages as "see a real report"), build.py, tests/, docs/; no X-Robots-Tag anywhere/raw/ also pollute the fetch-count usage metric (sw.js:26, docs/usage-metrics.md)vercel.json header rules — X-Robots-Tag: noindex for /raw/(.) and /prompts/(.) (they stay fetchable for agents, which is their purpose), and consider the same for root *.mdbuild.py:758–787 (page()), mirrored in template.html, studio.html, vitals.html: no og:site_name, no og:image:width/height/alt, no twitter:title/twitter:descriptionpage() and once in each hand-authored headb/47.html (and siblings): h2 sections contain h4 card/lens titles with no h3 (phases do use h3, so the outline is inconsistent rather than flat)h1 per page and descriptive link text are already in place everywhere checkedh4s to h3 in brief_detail/playbook_detail (pure CSS restyle, no visual change)examples/index.html; the site's proof page becomes shareable. (S)studio.html and vitals.html. (S)<lastmod> in the sitemap writer (build.py:1393). (S)X-Robots-Tag: noindex headers for /raw/ and /prompts/ in vercel.json; also cleans the usage metric. (S)og:site_name + image dimensions + twitter title/description in page(). (S)/b/ links in template.html closes most of the discovery gap this week; the full static-card render can follow. (M, biggest reach)Reach-blockers: none shipped today — nothing is unindexable and no noindex exists. SEO-1 is the closest thing: not a blocker for Google, but a real reach ceiling for every crawler that doesn't execute JS. Everything else is polish ranked by the traffic it touches.
Which of these fixes should I make? SEO-1 (static homepage catalog) is the highest-reach; the five quick wins (SEO-4, 5, 7, 8, 9) are a small, safe batch — or name any subset and I'll start there.