Repository navigation
Make the public pages readable for AI answer engines (AEO) - #156
Conversation
Implements the 11 answer-engine recommendations across the landing page, the 36 guide pages, robots.txt and llms.txt. - Every indexed page except /terms and /privacy opens with the reader's main question as its first H2 (#short-answer) and an answer of at most 100 words that says when another tool fits better. Guide answers live in scripts/seo_answers.py and use only facts their page already states. - The landing H1 slogan becomes "Prepare Cases and Practice Rounds with AI", and the subheadline is the definition: "DebateLab is an AI debate prep and practice app for students and competitive debaters that ...". The same sentence closes every guide and opens llms.txt. - Prices, plan facts and competitor facts carry "As of October 2026", driven by factsCheckedOn in src/config/site.json. - The landing FAQ uses native <details>, so its answers are in the prerendered HTML. Radix rendered closed answers as empty, which kept them out of the raw HTML; the accordion and its dependency are removed. - The pricing, WinDebate and DebateClub pages are rewritten from facts checked on the vendors' own sites. DebateClub turns out to target workplace conversations, not competitive debate, and the page now says so. - Guides get header/main/article/aside/footer landmarks, a breadcrumb nav, visible Published and Updated dates that match their JSON-LD, datePublished, and the lead Q&A in FAQPage. JSON-LD no longer passes through html.escape, which had put & and ' into two pages' data. - Landing JSON-LD adds both offers (trial and Pro), featureList, inLanguage and a FAQPage built from the visible questions. - robots.txt names meta-webindexer and the other AI crawlers in their own group, which repeats the private-route rules because a named group replaces *. llms.txt is now generated with the key facts and every guide. - scripts/check-site.mjs runs in the build and fails on broken internal links, fragments, sitemap or llms.txt URLs (applying the vercel.json redirects), bad JSON-LD, a missing question H2, images without alt, unnamed buttons and unlabelled form fields. - Decorative landing icons are aria-hidden; the mock-UI title in the hero preview is no longer an H3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reachedEnable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Next included review available in 45 minutes. View limit details
Note 🎁 Summarized by CodeRabbit Free
Comment |
|
CI on b7fe946: every Actions job (quality, docs, browser-smoke, database-tests, the four suite legs) failed in 2-3 seconds with zero steps. GitHub's annotation says: "The job was not started because recent account payments have failed or your spending limit needs to be increased." These are billing failures, not code failures. The same checks ran locally on b7fe946: docs:check, lint, typecheck, test (305), test:collaboration (30), test:edge (356), build (with check-site), test:sw, Playwright @smoke desktop (30) and the landing, public-page and Hungarian specs on desktop and mobile (44). database-tests was not run locally because this PR changes no migrations or database code. Both Vercel deployments built successfully. |
Codex review r1 on #156 found three checker gaps: - a field nested in its <label> was reported as unlabelled; - /share/:path* did not match /share, unlike Vercel; - any non-empty aria-labelledby counted as a name, even pointing at no element. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex review round 1 (b7fe946) had three findings, all in scripts/check-site.mjs, and all fixed in 77f1765:
Proof: planted |
Codex review r2 on #156: - labelledby text now includes nested elements, not just text before the first closing tag; - a labelledby list names the element if any referenced element has text; - implicit labels count by field position, so an identical unlabelled input elsewhere on the page is still caught; - aria-label="" no longer passes by matching into the next attribute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex review round 2 (77f1765) had four findings, all in scripts/check-site.mjs, and all fixed in 00c5f56:
Proof: planted all four cases plus the round-1 cases in a built page. The checker reported exactly the five broken ones: the button labelled by a missing id, the button with a blank aria-label, the unwrapped twin input, the input with |
|
Codex review round 3 on 00c5f56 found no issues. It ran 22 focused probes of the round-2 checker fixes. Every finding from rounds 1 and 2 is fixed (see the replies above). PR is ready for Adam's review; holding unmerged as requested. GitHub Actions jobs still do not start because of the account billing block, so all checks are local. |
# Conflicts: # src/components/landing/Footer.tsx







Preview (tailnet): https://codex-remote.tailab0ab3.ts.net:9100/ serves the production build of this branch. Guide pages open at
/<slug>/index.html, for example/debatelab-pricing/index.html. Sign-in does not work on the preview origin. Screenshots and recordings are in this comment.Review: Codex rounds 1 and 2 found 7 checker issues, all fixed in 77f1765 and 00c5f56. Round 3 on 00c5f56 found none.
Implements Rob Hoffman's 11 answer-engine (AEO) recommendations on the public site: the landing page, the 36 guide pages, pricing, the comparison pages, robots.txt and llms.txt. AI agents such as Meta's crawler, ChatGPT, Perplexity and Claude now find what DebateLab is, who it is for, what it costs and when another tool fits better in the raw HTML, without running JavaScript.
Do not merge yet. Adam asked for one PR, held for review. GitHub Actions is out of minutes, so every check below ran locally on the head commit. A local pass is not a CI pass.
The 11 items
aria-hidden, so screen readers and agents skip them. The hero's mock-UI title is no longer a stray<h3>between the H1 and the first H2. Every form field on public pages already had a<label>(hero motion field, login email and code). The only images are the logo (alt "DebateLab logo") and the social card. No product fact appears only inside an image. A new build check (see item 9) fails on an image without alt text, a button without a name or a form field without a label.<details>. Radix renders a closed answer as an empty element, so none of the FAQ answers were in the prerendered HTML before. They are now. Checked withcurlon the served build: the definition, "$5/month + tax", "3-day free trial", "No credit card required", "no permanent free plan", "11 prep modes", the three languages, the cancellation rule and "As of October 2026" are all indist/index.html. The guide pages are static HTML and already had their copy inline.#short-answer) as its first H2.scripts/seo_answers.pyuse only facts their own page already states.llms.txtand is the JSON-LD description. It is translated into Spanish and Hungarian.llms.txt. Competitor facts on the comparison pages carry the same date. The date comes fromfactsCheckedOninsrc/config/site.json. Copy that names the month itself (inseo_guides.pyandseo_answers.py) needs a manual edit, and docs/operations.md says so.User-agent: *, so the AI group repeats the private-route disallows (/app,/login,/share/and the rest). Without that, the named group would have opened the app to those bots. The Sitemap line stays. The generator now writesllms.txtwith the definition, dated key facts, a note that DebateLab is a separate product from debatelab.ai, and every guide.scripts/check-site.mjsruns innpm run build. It reads every HTML file indist/, the sitemap andllms.txt, resolves each internal link through thevercel.jsonredirects and rewrites, and checks#fragmentsagainst the target page's ids. I proved it with planted faults: it caught a missing page, a missing fragment, a missing question H2, an image without alt, an unnamed button and an unlabelled field, and it passed a redirected link, a trailing-slash link and an app route. Run againstmain's guide pages, it found no broken links. It did find HTML entities (&,') inside the JSON-LD of /ai-debate-coach and /debate-rebuttal-practice, because the generator passed the JSON throughhtml.escape. That is fixed. On production, all 13 consolidated-guide redirects return a single 301 to the right page, and every sitemap URL returns 200.<header>, the footer links sit in a labelled<nav>, and the FAQ uses<details>/<summary>. Guides now use<header>,<main>,<article>(with a<header>for the hero), a breadcrumb<nav>, an<aside>for About, a<nav>for related guides and<footer>. No public page has a clickable div. Every control is a real<a>or<button>.<time>elements. July 5 is the date of the commit that first added each guide (git log --diff-filter=A); all 36 share it. JSON-LDdatePublishedanddateModified,article:published_timeandarticle:modified_time, and the sitemap<lastmod>all match.CONTENT_DATEmoves to 2026-10-20 because every guide's copy changed.JSON-LD. The landing SoftwareApplication now lists both offers (the $0 trial and Pro at $5 a month before tax, as a
UnitPriceSpecification),featureList(the 11 modes) andinLanguage(en, es, hu). It also gets a FAQPage built from the visible questions. Guides add the lead Q&A as the first FAQPage entry. Pricing, the three comparison pages and best-ai-debate-tools also carry the SoftwareApplication.Competitor facts and where they come from
I checked each claim on the vendor's own site on 2026-10-20:
llms.txt. I did not write a dedicated comparison page; that is a possible follow-up.Other changes
@radix-ui/react-accordionandsrc/components/ui/accordion.tsxare removed (the FAQ was their only user), along with the accordion keyframes. The FAQ loses its height animation. The chevron still rotates.<a>tags, because these are static pages, not SPA routes.index.htmland the landing meta description now use the definition.llms.txt, robots and the new build check.Verification (local, head commit)
npm run docs:check,lint(including knip),typecheck: pass.npm test: 305 passed.npm run test:collaboration: 30 passed.npm run test:edge: 356 passed.npm run build: passes, andcheck-sitereports 41 HTML files with no problems. The sitemap has 39 URLs, all with lastmod.npm run test:sw: passes.@smokeon desktop Chromium: 30 passed.loading-performance,public-pagesandhungarian-parityon desktop and mobile Chromium: 44 passed. I updated two specs that pinned the old headline. The Hungarian spec now also checks the localized "2026. október állapot szerint" date.vite preview, the landing, short answer and FAQ open/close work on desktop and iPhone 12. Mobile has no horizontal overflow. The pricing and comparison guides render with their dates and question H2.Not run: the
agentic/suite, which calls paid OpenRouter models to judge pages (the copy changed, so its replay cache may need re-recording), and theverify-debatelabstack. No recipe covers the landing page, and this PR does not touch the login and app code. Nothing has reached staging or production.Screenshots and recordings are attached below.
🤖 Generated with Claude Code