Repository navigation
Conversation
…whole course on one page (#1703) Owner, 2026-10-19: "make every course free! our task is to grow the language"; "advertise TRI CLAW in the course footer"; "make the page with the course's topic fit on one page ... and make it a widget of widgets". A. Every course free - specs/ladder/course-access.t27: gHashTag/t27 VERSION 3 byte for byte (t27#10799, merge 242ce553); courseAccess.logic.ts rebuilt from it with 999 scripts/t27-logic.mjs (spec-sha256 edd87ade), courseAccess.gen.ts with t27c gen-ts. - courseLadder.ts: a stored course's step is the spec's step_of for its rung, never the step stored with it, so a course opened on a paid rung under VERSION 2 does not close when the club ends (the spec's own instruction to hosts). Test with a mutant that reads the stored step. - The lock, the gate, the club teaser and the rouble/TON pay forms are gone from Course.tsx, with their words, CSS and the checkout client. B. TRI CLAW, three paths, in the footer of every course page - Words and addresses in specs/ladder/course-ladder.t27 (+ RU bundle): own key -> t.me/<bot>?start=app_keys, the club -> ?start=app_club, the GitHub App -> its post while CLAW_APP_PUBLIC is false. The bot is the rules' BOT; no price in the words. C/D. The whole course on one page, the course deck, the layout numbers - specs/view/course-view.t27 (+ generator, RU bundle, tests): reading width per language from a measured character width, breakpoints, the frame budget (3 live, mount 600 px, unmount 2000 px), finger, focus, contrast. Course.css may name only its breakpoints. - scripts/course-onepage.mjs: <share path>all/ per course and language, lazy frames through public/widgets/frame-pool.js, contents column or sheet with progress dots, #lesson-<n>, share, print CSS. - specs/widgets/course-deck.t27 + public/widgets/course-deck: one widget for every course (?course=), nine module tabs, three lesson frames each; gallery entry before the wars pin; opened as "also" by t27-basics' last lesson so the courses still cover the gallery. - Lesson: text and widget side by side from 1150 px, stacked below with the widget right after the task; prev/next under the thumb; 44 px targets; visible focus; reduced motion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k
…ards and the deck page (#1703) Written by scripts/course-pages.mjs, scripts/widget-pages-from-spec.mjs, scripts/widgets-from-spec.mjs and scripts/course-from-spec.mjs from the specs of the previous commit; nothing here is edited by hand. - 22 new pages: learn/<course>/all/ and ru/learn/<course>/all/ (learn/all/ for the first course), each with its own 1200x630 card and ?v= stamp, and in learn/sitemap.xml with its hreflang pair. - Every course and lesson page: the one-page entry and the TRI CLAW block. - widgets/course-deck/: its page and courses/<id>.json (12 files). - widgets/gallery.t27: the course deck before the wars pin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k
…hat is also a widget (#1703) The course PR carries its post (specs/policy/course_post.t27): EN body and RU ruBody, registered in index.ts and posts.ts. Its figure is drawn by the compiled step_of of course-access.t27 VERSION 2 (8e535bc) and VERSION 3 (this branch), rungs 0 to 10. The numbers in it are this branch's measurements (3 live frames at most, layout shift 0, 0.43/0.53 em per character) and the spec's constants. It names what the change does not do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k
X Brain Health CheckScore: 100.0/100 X Brain is above merge threshold |
…e is free, the deck refuses an unknown course, the view spec holds what it claims (#1703) - Navigation: the Courses note is the ladder spec's SAY_PROMO_TITLE and its Russian ("Every course is free, no sign-up"), not the old "any three free" literal. - specs/catalog/onboarding.t27 COURSE_NOTE (it feeds /llms.txt and /agents.t27): every course is free, the one-page address; regenerated with onboarding-from-spec.mjs, check:onboarding green. - course-deck: ?course=<unknown> shows SAY_NOT_FOUND instead of t27 basics (pick.js, a pure function; test in course-pages.test.mjs; checked in Chromium for en and ru). - course-view.t27: FOCUS_OFFSET_PX and SETTLE_MS (the frame pool's look after a scroll, now a required parameter of frame-pool.js) join the spec, the deck's K_FOCUS_RING_PX/K_FOCUS_OFFSET_PX/K_SETTLE_MS are held equal to them, the one page's progress bar takes --course-read, and CONTRAST_MIN_X10 is dropped: contrast is measured on the rendered pages, not held by the spec. The generator header and the post now say the spec holds the layout numbers this change decides, not every number. - TRI CLAW's GitHub line says "built to check" in both languages. - CI: Course pages, Course ladder and Course view are their own steps (if: !cancelled()), so the red test:course #29 no longer hides them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k
…ery page's footer (#1703) checks (pure gates) on cb598ae failed at "The HUD can be read while the map is lit" (check:queen-contrast): Footer.tsx imported claw.css and courseView.generated.css, so src/App.tsx (the landing and the board) reached two stylesheets the gate had never measured. The block is only drawn on course pages, so it moves to components/ClawPromo.tsx, which imports both sheets; Footer takes an `aside` node and imports neither. Course.tsx and Courses.tsx pass <ClawPromo />. The gate's REACHED list is unchanged: App.tsx again reaches exactly what it did on main. check:queen-contrast, check:queen-fallback-parity and every other step of the pure-gates job pass locally; only Course (test:course #29) is red, as on main. Chromium: one TRI CLAW block on #/courses and on a lesson, none on #/ or #/blog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k
🧪 Brain Stress Test ResultsTests: 261/261 passed, 0 leaked |
|
Generated by Claude Code |
Closes #1703.
Owner, 2026-10-19: «сделай курсы бесплатно всех! наша задача язык раскачать!!», «в курса футере рекламируй подписку на TRI CLAW» (free with your own API key, more keys can be added; whoever does not want the hassle pays us; and the footer links to installing the TRI CLAW GitHub App), and «сделай чтобы страница с темой курса вместилась в одну страницу … и сделай эту страницу также виджетом, который содержит другие виджеты».
One PR, five commits: the source (A–D), the regenerated static pages and cards, the blog post a course PR carries, and two commits of fixes from the review of this PR (below).
A. Every course free
specs/ladder/course-access.t27is gHashTag/t27specs/automation/course-access.t27VERSION 3 byte for byte (t27#10799, merge 242ce553).src/lib/courseAccess.logic.tswas rebuilt the way it was made, with 999scripts/t27-logic.mjs(t27c from t27 master, Zig 0.16): 9 fns lowered,spec-sha256: edd87ade…= the vendored file.courseAccess.gen.tsist27c gen-tsoutput.step_ofis FREE for every rung.courseStateno longer judges a stored course by the step stored with it: its step isstepOf(rung), the spec's own fn (VERSION 3 tells hosts to do exactly this). A course stored on a paid rung under VERSION 2 stays open when the club ends. Test:scripts/course-ladder-from-spec.test.mjsbundles the realcourseLadder.tsover a fake store; the mutant that reads the stored step turns it red.LadderGate,ClubTeaser,PayForm,TonWait, the checkout client (checkoutOffer,startCheckout,startTonCheckout,readOrder),freeLeft,startLink, the promo/placement plumbing nothing reads any more, 40 words and their RU, and their CSS. The only key machinery kept is the onemay_reportneeds.course-ladder.t27words now say every course is free (SAY_LADDER, catalog, advert), RU bundle in step (every key, same digits).B. TRI CLAW in the course footer, three paths
Words and addresses in
specs/ladder/course-ladder.t27+i18n/course-ladder.ru.json; the generator joins the rules'BOT(t27ai_bot) into the links, so nothing restates the handle.https://t.me/t27ai_bot?start=app_keyshttps://t.me/t27ai_bot?start=app_club(no price in the words; the bot shows it)https://t27.ai/blog/tri-claw-an-agent-you-can-audit/keysroute, which another bee adds in gHashTag/999-multibots-telegraf in parallel. Until it ships,?start=app_keysopens the bot.tri-clawis confirmed by t27docs/claw/README.md(github.com/settings/apps/tri-claw) andspecs/claw/github_surface.t27. I could not confirm that it is public: from this sessiongithub.com/apps/tri-clawandapi.github.com/apps/tri-clawanswer 403,docs/clawhas noapp-manifest.yml,RELEASE.mddefers the Marketplace listing, andgithub_surface.t27measures the App on the gHashTag account only. So, as instructed, the third button opens the TRI CLAW post.CLAW_APP_PUBLIC : bool = falsein the spec picks it; flipping it (and its spec test) to true switches the button tohttps://github.com/apps/tri-clawwith the "Install TRI CLAW on your repository" words, already in both languages. A test holds both states.ClawPromo,src/styles/claw.css) shows on every course page, lesson page and the catalog in the app, and on every static course, lesson and one-page view (the same sheet, inlined).C. The whole course on one page, and a widget of widgets
scripts/course-onepage.mjs, called bycourse-pages.mjs, writes<share path>all/for every course and language:t27.ai/learn/all/(t27 basics),t27.ai/learn/<course>/all/,ru/before each. 22 pages, each with its own 1200×630 card (?v=stamp), canonical, hreflang pair, JSON-LD and a sitemap entry. It carries the intro, then every module, then each lesson's title, goal, text, task and widget.public/widgets/frame-pool.js(one implementation for the page and the deck). A frame mounts when its slot is withinMOUNT_MARGIN_PX600 of the viewport, at mostLIVE_FRAMES_MAX3 are live (the farthest gives way), a frame further thanUNMOUNT_MARGIN_PX2000 is taken down. Each slot holds the frame's height before it loads.← · Contents 08/27 · →that opens a sheet with the same list. Deep links#lesson-<n>, share for the page (native share, copy, X, Telegram, LinkedIn, utm-tagged byshareTags.ts) and per lesson (its own static page), mark-as-done in the same browser key as the app, print CSS.specs/widgets/course-deck.t27+public/widgets/course-deck/, data incourses/<id>.jsonwritten bycourse-pages.mjs. Nine module tabs, the module's three lessons as a scroll-snap row, each card nesting its lesson widget through the same frame pool (K_LIVE_FRAMES_MAX3 = one module). One generic widget (?course=<id>&module=<n>&lang=ru) instead of eleven: the eleven would differ only in data, and a twelfth course would need a twelfth widget, spec, card and gallery entry; this one needs nothing. Its K_ numbers are the view spec's, held equal bycourse-view-from-spec.mjs; no K_ holds a spec fn result, nologic.js,widget-logic --checkunaffected.the_course_deck_closes_the_course_shelves_before_the_arena.course-from-specrequires the courses to cover the whole gallery, so t27-basics lesson 27 ("Where next") opens the deck as its "also" widget, the one-word spec change that makes this a course PR (hence the post).#lesson-<n>), every static course and lesson page.D. UI/UX, the layout numbers in
specs/view/course-view.t2768chwould have been ~95 characters: achis a zero, and Outfit's zero is 0.66 em. Measured in Chromium over three courses at 18 px: an average character is 0.427–0.432 em in English (Outfit) and 0.531–0.532 em in Russian (system fallback). SoREAD_EM_X10_EN301 and_RU372 (30.1 em / 37.2 em ≈ 70 characters); the spec's tests hold both inside 65–75 characters, and:lang(ru)picks the wider one.SIDE_BY_SIDE_MIN_PX1150 (tested: two gutters + gap + the Russian column +WIDGET_MIN_PX400 fit); below it text, task, then the widget, then the mark. Contents column from 1440.NARROW_MAX_PX= the site'sPHONE_MAX600 andTOUCH_TARGET_PX= the site'sTOUCH_TARGET_MIN_PX44 (cross-checked against the vendoredspecs/ui/viewport.t27).Course.cssmay name only the spec's breakpoints (900/520 became 1149/600; a stray width fails the check).THUMB_BAR_PX56. 44 px targets for chips, buttons, crumbs links, notes links. Reserved frame height.:focus-visibleringFOCUS_RING_PX2.prefers-reduced-motiondrops transitions and smooth scroll. Greys under 4.5:1 (#6f6f6f,#5a5a5a) raised to#a6a6a6/#8a8a8a.Fixes from the review (commits f6705ac and 301e5f1)
SAY_PROMO_TITLE/ its Russian ("Every course is free, no sign-up"), no longer the "any three free" literal (checked in Chromium, en and ru).specs/catalog/onboarding.t27COURSE_NOTE(feeds/llms.txtand/agents.t27) now says every course is free and names the one-page address; regenerated withonboarding-from-spec.mjs,check:onboardingandtest:onboarding-specgreen.SAY_NOT_FOUNDfor?course=<unknown>instead of t27 basics:public/widgets/course-deck/pick.js(pure), tested incourse-pages.test.mjs, checked in Chromium (?course=nope→ "This course is not in the catalog." / «Такого курса нет в каталоге.», 0 frames).course-view.t27:FOCUS_OFFSET_PX2 andSETTLE_MS120 (the pool's look after scrolling stops, now a required parameter offrame-pool.js) are spec constants with tests; the deck'sK_FOCUS_RING_PX,K_FOCUS_OFFSET_PX,K_SETTLE_MSare held equal to them; the one page's progress bar takes--course-readinstead of 760 px.CONTRAST_MIN_X10is dropped: contrast is measured on the rendered pages (table below), not held by the spec. The generator header and the post now say the spec holds the layout numbers this change decides; type sizes, paddings and colours stay in the stylesheets.Course pages,Course ladderandCourse vieware separate steps withif: ${{ !cancelled() }}, so the redtest:courseАнализ проекта для ученых #29 no longer hides them.checks (pure gates)on cb598ae failed at "The HUD can be read while the map is lit" becauseFooter.tsximportedclaw.cssandcourseView.generated.css, sosrc/App.tsx(landing and board) reached two sheets the gate never measured. Root cause fixed rather than the list widened: the block moved tocomponents/ClawPromo.tsx(which imports both sheets),Footertakes anasidenode and imports neither, andCourse.tsx/Courses.tsxpass<ClawPromo />. App.tsx reaches exactly what it did on main. Every step of the pure-gates job now passes locally exceptCourse(Анализ проекта для ученых #29, as on main); Chromium shows one TRI CLAW block on#/coursesand on a lesson, none on#/or#/blog.Playwright (Chromium 1194; 390×844 touch, 1280×800), re-run on 301e5f1
…/all/#lesson-8lands with the lesson 12 px from the top and its frame live. The contents sheet lists 27 lessons with 27 dots; tapping lesson 20 closes it, the bar reads 20/27; marking it done fills its dot and the counter reads "1 of 27 done". At 1440 the contents column is 240 px, sticky, and marks the lesson in view; the bar is gone.prefers-reduced-motion: reduce→scroll-behavior: auto. Tab focus draws a 2 px solid ring.<link rel=preload>it is 0.learn/relu/) scrolls 25 px sideways at 390 px on main as well as here; the cause is not found.Screenshots (session scratchpad
onepage/shots/):onepage-neural-networks-390-top.png(the intro, the "free" pill, the bottom bar),onepage-neural-networks-390-lesson8.png(lesson 8 stacked: goal, text, task, the widget right after),onepage-neural-networks-1280-lesson8.png(side by side),onepage-1440.png(the contents column with dots),onepage-390-contents.png(the contents sheet),lesson-390-footer.pngandlesson-1280-ru-footer.png(the TRI CLAW block, three paths),lesson-390.png(compact crumbs, thumb bar),deck-390.png/deck-1280.png(the deck embed, module tabs, live lesson widgets),blog-390.png(the post's figure).Checks (apps/website, local, on 301e5f1)
npm run build✓ (the 8 JSON files the prebuild rewrites restored) · a secondgen:course-pagesleaves no diff.website-checks.ymlrunsCourse(check:course, test:course), thenCourse pages,Course ladderandCourse viewas their own steps;course-view-from-spec.mjsis also inprebuild.🤖 Generated with Claude Code
https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k