Skip to content

feat(course): a static page and preview card for every lesson - #1428

Merged
gHashTag merged 1 commit into
mainfrom
feat/course-og
Oct 5, 2026
Merged

gHashTag merged 1 commit into
mainfrom
feat/course-og

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 5, 2026

Copy link
Copy Markdown
Owner

A link to t27.ai/#/course previewed 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.mjs generates 62 static pages from course.t27: /learn/, /learn/<id>/, /ru/learn/ and /ru/learn/<id>/. Each page has:
    • the lesson's own goal, text and task;
    • canonical, reciprocal hreflang (en/ru/x-default), og + twitter summary_large_image, and JSON-LD (Course / LearningResource);
    • a link into the interactive lesson;
    • no redirect.
  • A 1200x630 card per page, drawn by headless Chromium and committed. It shows the module, "Lesson n of 30", the title, the goal, the lesson widget's own card and a 30-cell progress strip. og:image carries ?v=sha12, so a new card gets a new URL.
  • /learn/sitemap.xml lists all 62 pages with hreflang alternates.
  • Gate: npm run check:course-pages runs 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.json records what each card was drawn from.
  • New UI strings SAY_OPEN_LESSON and SAY_OPEN_COURSE go 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

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>
@github-actions github-actions Bot added the status:in-progress 🔵 Agent working label Oct 5, 2026
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

X Brain Health Check

Score: 100.0/100
Status: 🟢 HEALTHY
Threshold: 80/100

X Brain is above merge threshold

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🧪 Brain Stress Test Results

Tests: 261/261 passed, 0 leaked
Status: 🟢 PASS
Gate: every test in zig build test-brain-stress passes and none leaks

@gHashTag
gHashTag merged commit 2b7af41 into main Oct 5, 2026
39 checks passed
@gHashTag
gHashTag deleted the feat/course-og branch October 5, 2026 14:09
@github-actions github-actions Bot added status:completed Done and removed status:in-progress 🔵 Agent working labels Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Course: a static page with its own preview card for every lesson (X showed the home card)

1 participant