Repository navigation
Conversation
Move the course-27 recipe into specs/course_recipe as two t27 modules and point AGENTS.md at them, with the blog post for this PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ness check npm run cards:widgets draws public/widgets/<id>/card.png in black and white from the widget spec's TITLE and DESCRIPTION and the gallery's words, in node alone: its own WOFF2 decoder (glyf transform, gvar for bold), scanline rasterizer and fixed-Huffman deflate, so the same spec gives the same bytes. Default: gallery tools with no card; --id <id>; --all. Writing a card re-runs widget-pages-from-spec (the og:image carries the card's hash). check:widget-cards fails on a gallery tool with no card and on a drawn card (tEXt Software mark) that no longer matches its spec; hand-drawn cards are held to being present. Wired into website-checks next to the widget gates. Also: blog post a-widget-card-drawn-from-its-spec, and one AGENTS.md line in the course recipe section pointing at cards:widgets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s of 1 The owner's rule was 9 modules of 3 lessons (27 lessons); the recipe had flattened it to 27x1. Constants, tests, roadmap and the AGENTS.md section now say 9x3; the roadmap's 9 parts per course are the 9 modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cipe # Conflicts: # apps/website/src/data/blog/index.ts # apps/website/src/data/blog/posts.ts
…feat/widget-cards-from-spec # Conflicts: # AGENTS.md # apps/website/src/data/blog/index.ts # apps/website/src/data/blog/posts.ts
Contributor
X Brain Health CheckScore: 100.0/100 X Brain is above merge threshold |
…m-spec # Conflicts: # .github/workflows/website-checks.yml # apps/website/src/data/blog/index.ts # apps/website/src/data/blog/posts.ts
Contributor
🧪 Brain Stress Test ResultsTests: 261/261 passed, 0 leaked |
Two post-registry entries began their slug: line inside the previous entry's ru.openQuestions array: the main-first + head-after union kept both conflict fragments but dropped the array/object closers that sat between them. tsc -b reports 24 syntax errors and the typecheck ratchet refuses the file (0 -> 24). Each seam gets the four closing lines a healthy entry boundary already carries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
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.
What
npm run cards:widgets(apps/website) draws the share cardpublic/widgets/<id>/card.pngof every widget tool inspecs/widgets/gallery.t27that has none.--id <id>draws one widget's card and--alldraws every one. The card is black and white, and its words come from the widget spec: TITLE, DESCRIPTION and CATEGORY, plus the gallery's TOOL_BRAND, CATEGORY_NAMES and ORIGIN. Nothing on it is drawn by hand.npm run check:widget-cardsfails when a gallery tool has no card, or when a drawn card no longer matches its spec. It runs inwebsite-checksas the "Widget cards" step, right after "Widget tool pages".How
scripts/widget-cards-from-spec.mjsdoes everything itself:3850b6cd...both times. A card is about 45 KB and takes 1 to 2 s.Software=widget-cards-from-spec, plusSource=specs/widgets/<id>.t27.--checkredraws marked cards in memory and compares bytes. The 27 hand-drawn cards on main have no mark, so the check only requires them to exist and be 1200x630; none is replaced here.widget-pages-from-spec.mjs, because a widget page's og:image link carries?v=<card sha>.scripts/widget-cards-from-spec.test.mjs, 6 tests:Proof (local, Node 22, on 5bdcf8e's tree)
rm public/widgets/gatle/card.png, thennpm run check:widget-cardsgives rc 1:1 problem(s), with the missing card named.npm run cards:widgetsdraws the card (46,566 bytes) and rewrites the 27 widget pages.check:widget-cardsis then rc 0, reporting1 drawn from spec and current, 26 drawn by hand and present.npm run cards:widgets -- --id gatledraws 0 cards: the sha-256 is unchanged, 3850b6cd...check:widget-cardsgives rc 1:public/widgets/gatle/card.png is stale: .... Thencards:widgets -- --id gatleredraws it, after which bothcheck:widget-cardsandcheck:widget-pagesare rc 0.git checkout --restores the spec, the card, the page and the spec copy. After that,check:widget-pagesandcheck:widget-cardsare rc 0 on the clean tree.Other checks:
npx tsc --noEmitis rc 0.Also in this PR
a-widget-card-drawn-from-its-spec, with an Englishbodyand a RussianruBody. It is registered at the top ofindex.tsandposts.ts.cards:widgets, under "New course to open a topic". That section is not on main yet; it arrives with docs(agents): the Queen's recipe for a new 27-lesson course #1465. This PR adds the heading plus the one line at the same place, so whichever of the two merges second has a one-hunk conflict: keep docs(agents): the Queen's recipe for a new 27-lesson course #1465's section and append this line.owner-approved-foreignlabel.Limits
{ "version": 1, "head_sha": "266446729eac0e46de8e73a5b6b92c94cfdbfbd9", "summary": "Widget share cards are now generated from each widget's t27 spec in black and white by a pure-node renderer, and a CI check fails on a missing card or a drawn card that lags its spec.", "changes": [ "apps/website/scripts/widget-cards-from-spec.mjs: WOFF2 + gvar font decoder, scanline rasterizer, own deflate PNG writer, CLI with --id, --all and --check", "apps/website/scripts/widget-cards-from-spec.test.mjs: 6 tests for deflate, PNG shape, byte-identical redraw, fonts and the check's verdicts", "apps/website/package.json: cards:widgets and check:widget-cards", ".github/workflows/website-checks.yml: Widget cards step after Widget tool pages", "apps/website/src/data/blog: post a-widget-card-drawn-from-its-spec (body and ruBody), registered in index.ts and posts.ts", "AGENTS.md: one line under New course to open a topic pointing at cards:widgets" ], "tests": [ { "command": "rm public/widgets/gatle/card.png; npm run check:widget-cards; npm run cards:widgets; npm run check:widget-cards", "result": "rc 1 naming the missing card, then one card drawn (46566 bytes) and pages rewritten, then rc 0", "status": "passed", "evidence": "local runs on the tree of 5bdcf8ea" }, { "command": "edit gatle TITLE; npm run check:widget-cards; npm run cards:widgets -- --id gatle; check:widget-cards and check:widget-pages; git checkout", "result": "rc 1 calling the card stale, redraw, both checks rc 0, tree restored and both checks rc 0 again", "status": "passed", "evidence": "local runs on the tree of 5bdcf8ea" }, { "command": "npm run cards:widgets -- --id gatle twice; shasum -a 256", "result": "second run writes 0 cards; sha-256 3850b6cd... unchanged", "status": "passed", "evidence": "local run on the branch head fec4d4fa1" }, { "command": "node --test scripts/widget-cards-from-spec.test.mjs and the blog-cover tests; npx tsc --noEmit", "result": "6 of 6 and 12 of 12 pass; typecheck rc 0", "status": "passed", "evidence": "local node --test summaries" }, { "command": "lefthook pre-commit and commit-msg", "result": "queen and explorer language contracts pass, subject is English ASCII", "status": "passed", "evidence": "hook output on 5bdcf8ea" } ], "limitations": [ "Byte equality across machines is argued from the arithmetic and not yet measured; the CI run is its first test", "None of the 27 hand-drawn cards on main is replaced, and a stale hand-drawn card still passes the check", "No kerning or hinting, and the fonts are Latin subsets, so the cards are English only", "Only widget tools are covered; lesson, player and recording cards keep their own generators", "The AGENTS.md section heading also arrives with #1465, so the second of the two to merge has a one-hunk conflict" ], "tags": [ "t27", "widgets", "cards" ], "blog": { "title": "A widget's share card, drawn from its spec", "summary": "Widget share cards are no longer drawn by hand: a node script draws them in black and white from the widget's t27 spec, byte for byte the same each time.", "outline": [ "All 27 widget tool cards on t27.ai were drawn by hand, and nothing checked that a card still said what its spec says.", "The card's title, line, brand and category come from the widget spec and the gallery spec, read through the site's own t27 compiler.", "A browser screenshot is not byte-stable, so the script decodes the fonts, rasterizes and writes the PNG itself with node alone.", "The proof deletes a card, then changes a title, and shows the check fail, the generator fix it and the check pass again each time.", "The post names the limits: one machine so far, no kerning, Latin fonts only, and hand-drawn cards held only to being present." ] } }🤖 Generated with Claude Code