Repository navigation
feat(course): a static page and preview card for every lesson - #1428
Merged
Merged
Conversation
Crawlers drop the '#', so t27.ai/#/course previewed on X as the home page. scripts/course-pages.mjs writes /learn/<id>/ and /ru/learn/<id>/ (62 pages) from course.t27: the lesson's words, canonical, hreflang, og/twitter, JSON-LD, a 1200x630 card rendered by headless Chromium, and learn/sitemap.xml. check:course-pages gates them in CI. Closes #1427 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
X Brain Health CheckScore: 100.0/100 X Brain is above merge threshold |
Contributor
🧪 Brain Stress Test ResultsTests: 261/261 passed, 0 leaked |
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A link to
t27.ai/#/coursepreviewed on X as the home page ("AGI game — play, direct, earn"): crawlers drop everything after#, so no lesson had a preview of its own or could be indexed.scripts/course-pages.mjsgenerates 62 static pages fromcourse.t27:/learn/,/learn/<id>/,/ru/learn/and/ru/learn/<id>/. Each page has:summary_large_image, and JSON-LD (Course/LearningResource);og:imagecarries?v=sha12, so a new card gets a new URL./learn/sitemap.xmllists all 62 pages with hreflang alternates.npm run check:course-pagesruns in the Course CI step. It refuses a page, card (size, bytes, drawn from other words) or sitemap that lags the spec. It needs no browser:cards.jsonrecords what each card was drawn from.SAY_OPEN_LESSONandSAY_OPEN_COURSEgo into the spec, plus RU.Closes #1427
{ "version": 1, "head_sha": "c1d0c9bcad4dd6fe198cc3007f687c5332d62f47", "summary": "Every course lesson now has a static page at /learn/<id>/ and /ru/learn/<id>/ with its own 1200x630 preview card, canonical, hreflang, JSON-LD and a sitemap, so a shared link previews as the lesson instead of the home page.", "changes": [ "scripts/course-pages.mjs: generates 62 pages, 62 cards rendered by headless Chromium, public/learn/cards.json and public/learn/sitemap.xml from course.generated.ts; --check verifies them without a browser.", "scripts/course-pages.test.mjs: one page per lesson per language, no redirect, own canonical and hreflang, card meta, the lesson words present, JSON-LD parses, sitemap lists each page once.", "specs/course/course.t27 and i18n/course.ru.json: SAY_OPEN_LESSON and SAY_OPEN_COURSE; generated files rewritten by course-from-spec.", "package.json and website-checks.yml: gen:course-pages and check:course-pages, run in the Course step." ], "tests": [ {"command": "node scripts/course-pages.mjs --check", "result": "up to date 62 pages (en, ru), 62 cards, learn/sitemap.xml", "status": "passed", "evidence": "Local run on this branch."}, {"command": "node --test scripts/course-pages.test.mjs", "result": "5 pass, 0 fail", "status": "passed", "evidence": "Local run on this branch."}, {"command": "node scripts/course-from-spec.mjs --check", "result": "30 lessons in 10 modules, 45 widgets; spec tests 6, asserts 24, all hold", "status": "passed", "evidence": "Local run on this branch."}, {"command": "headless screenshot of /ru/learn/bits-and-trits/ and of the EN and RU cards", "result": "page and cards render; long RU titles and the longest URL fit", "status": "passed", "evidence": "Screenshots read locally."} ], "limitations": [ "X caches a card per URL: a post that already links t27.ai/#/course keeps its old card; new posts should link t27.ai/learn/ or t27.ai/learn/<id>/.", "robots.txt on the apex does not list learn/sitemap.xml yet; that is a separate change in the apex repo." ], "tags": ["Course", "Website", "SEO"], "blog": { "title": "Every t27 lesson now has its own preview card", "summary": "A course link on X showed the home page card because crawlers ignore the hash; each of the 30 lessons now has a static page and a card drawn from the course spec.", "outline": [ "Why a hash route such as t27.ai/#/course is invisible to X, Telegram and search engines, and why a redirect page would not fix it.", "How one generator reads course.t27 and writes 62 pages, 62 cards and a sitemap, in English and Russian, with nothing written twice.", "How CI tells a stale card without a browser: the manifest records a hash of the card HTML and every picture and font it loads." ] } }🤖 Generated with Claude Code