How Kevin Liu runs General Translation work: the principles, the bar, the playbook, the product map, the glossary and the decisions, then the canon from the agent guide to the libraries.
This handbook records how Kevin Liu runs his work at General Translation (GT), a localization platform for developers. It is written for the agents and people who work with him: Claude Code and Codex sessions, teammates, and anyone who copies this repository into another project to work the same way. The skills hold the procedures for each area of the work. The handbook holds what spans them: the standing principles, the bar for done, the rules for running many sessions at once, the product facts, the vocabulary and the dated rulings.
The handbook is served at /handbook on each of the site's three addresses (www.prototemplate.com, the canonical one, and the aliases prototemplate.vercel.app and prototemplate.kevinliu.studio), and the files live in docs/handbook/ of the public repository. On the site the book continues with the canon after the six documents below: the agent guide, BRAND.md, DESIGN.md, ARCHITECTURE.md, the ship loop, the tools, the libraries with the build log, the graphics pipeline and the artifact pictures. Each document also answers as Markdown at its address plus .md. The docs site at /docs explains in ten pages how the hub works.
| Document | What it holds | Read it |
|---|---|---|
| Operating principles | The eighteen standing rules behind Kevin's corrections, each with how to apply it, the skill that holds its procedure and the dated directive it comes from | Before the first task of any GT work |
| Quality bar | The measurable bar for each kind of artifact and for done, each row with the skill that owns it | Before showing Kevin work or shipping it |
| Multi-session playbook | Lanes, forked sessions, one instruction sent to several sessions, shared checkouts, collisions, Claude and Codex on one main, relay notes and shared resources | When another session works on the same machine or repository |
| GT product and architecture map | What GT sells and to whom, how the products fit together, the copy that must be exact, the CLI and agent entry points, and the repositories | Before writing about, designing for or changing a GT product |
| Glossary | The terms in Kevin's messages, the skills and the code, each with where it lives | When a word in a request or a skill is unfamiliar |
| Decisions log | Kevin's dated rulings, what each replaced, his words and the lint, gate or skill that holds it | Before changing a standard, and when two sources disagree |
AGENTS.md at the repository root is the entry point for an agent. It summarizes the principles, routes each kind of task to its skill, and points here for depth.docs/handbook/quality-bar.md). A project that installs the skills copies this folder and AGENTS.md beside them, as README.md's "Import this into another project" section describes.gt-voice: plain declarative sentences, no em dashes, no metaphors, and no contrast pairs.src/app/handbook/registry.ts and its headings in HANDBOOK_HEADINGS (src/lib/search-index.ts). The prototemplate skill (section 6) lists every step, and pnpm lint:registries reports what a change missed.General Translation (GT) is a localization platform for developers. Kevin Liu does design, website and product work at GT, and he runs it through several Claude Code and Codex sessions at once. The eighteen rules below come from his corrections to that work between 2026-07-20 and 2026-10-08. Read them before the first task of any GT work with him.
Each rule names the skill or document that holds its procedure. Where the two disagree, the skill is right and this page needs a fix. The glossary defines working terms such as lane and feature flag. The quality bar sets the bar for each kind of artifact, and the product map describes what GT sells and how its products fit together.
Run every wave of a plan to completion without stopping between waves to ask for permission. "Continue", "continue all" and "keep going" resume every open lane and every unfinished item. When an agent dies on a usage limit, an outage or a closed app, check the working tree, then resume the agent with its brief intact (gt-orchestration section 6). Do the hard items as well and report each one. Spend the time on the fixes, and write tests to prove them.
Kevin, 2026-09-09: "can we just do all of our updates? why are you holding back". He said the same on 2026-07-24 and 2026-09-12.
Do every step an agent can do, including console setup through a CLI (vercel, gh, gcloud) or the browser page Kevin already has open. Collect what only he can do (a login, a payment, a credential consent, an approval, a Linear issue id) into one numbered list with the exact file, line or URL for each item. A blanket approval covers the plan it answers. The safety lines still hold. Before a step that cannot be undone, such as a merge over recent work, check that nothing is lost. Wait for his word on a force push, a production deploy to GT's Vercel team, new spend, or landing an exploration round on Prototemplate main (gt-ship section 8).
Kevin, 2026-10-05: "anything approved is approved from me." Also 2026-07-20 and 2026-10-02.
Make the smallest literal change that answers the request. "Move it down" means translate it and keep its angle and easing. When a request could mean a small or a large change, make the small one or ask. When a fix spreads into neighbouring behaviour, assets or metadata, revert the extra changes exactly and apply the narrow change. An image Kevin pastes is the whole spec.
Kevin, 2026-08-06: "why did so much change, i just wanted to alter the connectors and line coming up". On 2026-08-07 a landing update had also redone the OG image, the favicon and the metadata, and he said there was no need.
Before changing a surface, list what Kevin praised on it and carry every item forward, because a rewrite that drops an approved element is a regression. When he rejects a direction, restore the files to exactly their state before the rejected change and rebuild from there, since the rejected version is never the base of the next attempt. Commit early in each round on a local branch so git holds the restore point, and archive each published version before overwriting it. The dated verdicts are in gt-aesthetic section 2, and the restore and archive steps are in gt-explorations section 6 (After a rejection) and section 7 (Archive before overwriting).
Kevin, 2026-08-07: "lets redo this to before i started asking for this change and rebuild". Also 2026-07-30 and 2026-10-03.
When Kevin flags one instance (an icon, a footer, a flag chip, a double border), enumerate the class programmatically with grep, a registry, a lint or a measurable check, fix every member, verify each one and report the count. The sweep repeats the requested change on each instance of the same defect, and rule 3 still holds for everything around them. When he likes a treatment in one place, apply it to the sibling components. The cross-app sweep is in gt-components (Standardization), and the fixes by failure class are in gt-lints section 6.
Kevin, 2026-09-28: "look for ANYWHERE ELSE icons nneed to be synced". Also 2026-08-03.
Diagnose before editing: read the computed values of the element Kevin selected, read the full log, and reproduce with the deployed environment. Fix the cause, then add the regression test or lint that keeps the class from returning (gt-lints section 4). Error handling keeps the underlying error in its message and never substitutes a guessed cause.
Kevin, 2026-08-18: "right but why is the fetch failing". Also 2026-08-05.
A fix is reported only with visual or measured proof of the rendered result at the place Kevin flagged: computed styles on the live element, 2x crops of corners and junctions, and every animation state. A code change does not prove the render, and neither does a green proxy check (the page loads, the sizes match) or a settings panel that shows the right value. When a tool reads badly, switch to another one, such as a Playwright capture, and finish the verification yourself. The methods are in gt-verify, and the quality bar (Done) states when work counts as finished.
Kevin, 2026-07-30: "no, the borders are still sstraight black, still padding, and borders wrong color". The fix before it had been reported as verified from a screenshot.
Results reach Kevin as something he can see or hear: before and after crops at the same viewport, the localhost URL with its server running, a page open in his browser, or a small HTML page for a set of images or audio takes. Send them before he asks. The formats are in gt-reporting section 1, the review servers and the steps for letting him try a build are in gt-local-dev sections 1 and 5, and the crop and PR rules are in gt-aesthetic (Showing the work) and gt-ship section 4.
Kevin, 2026-10-05: "i cant listen to the voices, make it in html so i can go through and listen to em".
Copy and numbers come from production, the benchmark and data files, or GT's published pages. Never invent testimonials, quotes, metrics, prices or feature promises, and mark placeholder content as placeholder. Marks, logos, flags and scans are the real files, sourced and credited: third-party logos from thesvg.org (gt-brand section 5), pictures with their source, license and credit (gt-dither section 5), and the search for an asset continues until the real one is found (gt-brand section 8, Third-party material).
Kevin, 2026-08-05: "like these arent real quotes", about a testimonial block an agent had written.
Ask Kevin only what is his to decide: spend, naming, licensing, scope, or an instruction that reads two ways. Ask each as a multiple-choice question with the recommended option first, and batch several into one numbered list with short ids so he can answer inline (gt-reporting section 6). When a new state resembles an existing one, reuse the existing behaviour and say so instead of asking. A gate that blocks a requested change is a question for him, and bypassing it silently is a defect.
Kevin, 2026-09-25: "no need to ask me anythiing just do this all for me please".
Treat a gap at startup, a flash on reload and lag on the first interaction as defects, even when the page settles a moment later. Check the first frame and the first interaction after a cold load and after a reload, and reserve space so nothing shifts (gt-verify, gt-performance).
Kevin, 2026-08-08: "if i reload i briefly see 3 layers stacked on top of each other". Also 2026-08-04, on an animation that had to start "without a startup lag".
A release carries no development code. Keep galleries, stand-ins, test-only hooks, dev routes, temporary tests, recorded fixtures and harnesses off the release branch (gt-ship section 3). Add no dependency that existing code can replace, keep comments to one or two lines, and keep an unready dashboard feature behind a feature flag.
Kevin, 2026-10-01: "we cant ship anythign unnnecessary". On 2026-08-28 he questioned three packages added for one feature, and since 2026-09-03 a dashboard page that is not ready ships behind a feature flag.
The same rule applies inside a change. Use the platform feature that removes code before adding code, such as a CSS grid row from 0fr to 1fr for an auto height in place of a measured one. When a review asks to test machinery that need not exist, remove the machinery. Kevin, 2026-10-06, on a caption card that measured its own height: "Beautiful but don't complicate. Always simplify."
Challenge any design that adds paid infrastructure, and lay out the free alternative with numbers first. Use the cheapest model that performs well enough, put spend caps on paid model runs (gt-orchestration section 7), and turn off git-triggered preview deploys where they only cost money: the landing builds main and staging only (gt-website section 8). Check which account a cost lands on before acting.
Kevin, 2026-09-15: "make a new pr to not allow preview deployments from git in landing, this is running up a lot of costs".
Keys and tokens live in a protected config file or a gitignored env file. They never appear in chat, URLs, prompts, commits, docs or PR bodies, and briefs and docs name them by variable name only. When Kevin pastes a key into chat, move it into the env or config file, never echo it or commit it, and tell him it now sits in a transcript so he can rotate it. Never ask him to paste a key into chat.
Kevin's brief, 2026-08-03: "never print a key value".
When Kevin asks to build out, enhance or update something fully, cover every section and capability with real diagrams, visuals and code, and state why the design works. A placeholder or a partial pass does not count.
Kevin, 2026-08-05: "show actual diagrams and visuialzations and code in these. enhance fully". Also 2026-08-04.
On a page that already exists, "enhance" keeps the page and adds to it: the copy, the sections and the pieces Kevin likes stay. Kevin, 2026-10-08, after a rebuild of the careers page dropped its hero and its copy: "keep the original write up".
After something works once, turn it into a skill, a doc, a lint or saved parameters: the prompts that worked and how they were structured, a lint for each standard, and generation parameters saved next to each asset. Every tool and skill belongs to a workflow with a routing doc. A new skill follows the contract in prototemplate section 10, and a new lint follows gt-lints section 4.
Kevin, 2026-10-05: "create docs and skills for audio creation and generation, what prompts worked and how u structured it". Also 2026-07-24 and 2026-08-06.
Agent tools, skills and scripts work with Codex, Claude Code and local models alike. Skills use the Agent Skills frontmatter fields only, so every loader accepts them (prototemplate section 10), and no script hard-wires one provider.
Kevin, 2026-09-10: "be vendor agnostic, dont just ennforce claude code".
Prototemplate is public at www.prototemplate.com and github.com/Kevin-Liu-01/Prototemplate. Nothing in it carries keys, account ids, email addresses, personal details, customer names beyond BRAND.md section 9, unannounced plans, or business numbers such as funding, revenue and headcount (gt-brand section 8, gt-voice "Facts and public surfaces"). Colleagues appear by role. pnpm lint:public scans every file for key shapes, machine paths and the terms on a private denylist, and the pre-push hook in .githooks/ runs it.
The rule dates from the brand questionnaire round (2026-08-06 and 2026-08-11) and from 2026-10-05, when Kevin made Prototemplate his public hub for GT work.
| Rules | Skill or document |
|---|---|
| 1, 13 | gt-orchestration: resuming agents, briefs, fan-out, spend caps |
| 1, 2 | Multi-session playbook: lanes, broadcast messages, relays between Claude and Codex |
| 2, 4, 12 | gt-ship: PR shape, release hygiene, Prototemplate landing and deploys |
| 3, 4 | gt-aesthetic: the reference, the dated verdicts, reading Kevin's asks |
| 4 | gt-explorations: exploration rounds, archive and restore |
| 5, 6, 16 | gt-lints and gt-components: lints, sweeps, fixes by class |
| 6, 7, 11 | gt-verify: measurement, the viewport matrix, live checks |
| 7, 15 | Quality bar: the bar for each kind of artifact and for done |
| 8, 10 | gt-reporting: crops, galleries, status lines, decision lists |
| 8 | gt-local-dev: review servers, letting Kevin try a build |
| 9 | gt-brand (Third-party material) and gt-dither: real assets, provenance, credits, licences |
| 11, 13 | gt-performance: frame cost, first interaction, Lighthouse |
| 13 | gt-website: landing deploys |
| 16, 17 | prototemplate: the skill contract and the hub |
| 18 | gt-brand, gt-voice and BRAND.md: public surfaces |
Kevin's wiki skill agent-iteration-loop holds the general loop of implementing, verifying, reviewing and shipping, and these rules apply inside it.
skills/gt-aesthetic sections 2 and 5; skills/gt-ship sections 3, 4 and 8; skills/gt-lints sections 1, 4 and 6; skills/gt-components (Standardization); skills/gt-brand sections 5 and 8; skills/gt-dither section 5; skills/gt-voice (Hard rules, Facts and public surfaces); skills/gt-website section 8; skills/gt-local-dev sections 1 and 5; skills/prototemplate sections 8 and 10; docs/handbook/quality-bar.md and docs/handbook/glossary.md.redesign-v0-verdict, pr-size-discipline, explorations-stay-local, plain-technical-english and sentence-order-rules, which hold earlier statements of rules 2, 3, 4 and 12 and the writing rules this page follows; always-simplify (2026-10-06) and careers-team-photo (2026-10-08), read 2026-10-10 for the additions to rules 12 and 15.Kevin Liu reviews General Translation (GT) design and site work. Work is ready to show him or to ship when it clears the bars below for its kind of artifact. Each bar names the skill that owns it, and that skill holds the procedure for meeting the bar. When this page and a skill disagree, the skill wins, and the change that finds the difference corrects this page. A bar that no skill holds yet lives on this page, and its Owner column says so.
deck/ and served at /deck, and the site is built from apps/landing on gt-cloud origin/main. From 2026-08-06 the Dossier (/d/singularity-dossier) governed the site (Kevin, 2026-08-06: "dossier is indeed our completed version and treat it as such"). On 2026-10-07 Kevin ruled "fix the dossier references", and the Dossier stays in the gallery as the direction the site grew from. Before the deck and the Dossier existed, the July rounds measured against generaltranslation.com and resend.com (Kevin, 2026-07-30).Owner is the skill or document that holds the bar, with the section to read. Read is the day the owner was read for this page. Where the owner is this page, Read gives the day Kevin set the bar. Paths are relative to the Prototemplate root, and the glossary defines terms such as plate, rail and the Dossier.
The rows apply to product surfaces, the site, Prototemplate's own pages and docs pages.
| Bar | Owner | Read |
|---|---|---|
| The page matches its reference measure for measure, and every value the reference lacks has a stated reason. | gt-aesthetic, section 1 | 2026-10-05 |
| Content comes from production or the prototype spec, and nothing is invented. | gt-aesthetic, review checklist | 2026-10-05 |
| A display head holds two lines at most at every width. | gt-aesthetic, section 4 | 2026-10-05 |
A lead reads in two or three lines at 60 to 70 characters, and prose stays under 75. node skills/gt-aesthetic/scripts/measure-type.mjs <url> --strict passes, or each flag has a reason. | gt-aesthetic, sections 1 and 4 | 2026-10-05 |
| Nothing overflows its column or the viewport, and no text is clipped. | gt-aesthetic, review checklist; gt-lints, check:pages | 2026-10-05 |
| Nothing shifts after first paint, and client-mounted rows reserve their height. | gt-aesthetic, review checklist; gt-lints, check:pages | 2026-10-05 |
Tap targets on a phone are 44px. check:pages fails a target under 40px and notes one of 40 to 43px. | gt-aesthetic, section 3 (Rhythm); gt-lints, check:pages | 2026-10-05 |
| Text a reader needs clears 4.5:1 in both themes. | gt-aesthetic, section 3 (Hierarchy and contrast) | 2026-10-05 |
Every line is drawn once by one owner, rules run edge to edge, and junctions pass at 2x crops. In Prototemplate's chrome each line takes the hair, hair-soft or edge role and pnpm lint:lines:shell is clean. Any other page passes pnpm lint:lines <url> in both themes. | gt-aesthetic, section 3; DESIGN.md, section 2; gt-lints, lint:lines | 2026-10-05 |
| The surface carries one dithered material, at the landing hero's strength or under it. | gt-aesthetic, section 3 (Material); gt-dither | 2026-10-05 |
| The page uses one accent, GT blue, and semantic color sits only on icons. | gt-aesthetic, section 3 | 2026-10-05 |
| Each width shows one composition. Intermediate widths and zoomed windows fall to the mobile composition, with no hybrid of desktop logic and mobile layout. | gt-verify, section 4 | 2026-10-05 |
| The longest translation of every string fits its reserved lines, and the layout holds in CJK, RTL and Indic text as well as Latin. | gt-verify, section 5; gt-aesthetic, section 4 | 2026-10-05 |
Both themes are shot at 1440 and 390 (plate pages also at 1527 by 814), and pnpm check:pages --preset quick --pages <id> shows zero defects on every touched page. | gt-aesthetic, section 5 (Local review); gt-lints, section 2 | 2026-10-05 |
A Prototemplate page with a book head follows the book page standard (nothing drawn above the title, the panel with Updated first, one band ruled on both edges), and its corners follow the radius law. pnpm lint:heads and pnpm lint:radius pass, live modes included. | prototemplate, sections 3 and 5; DESIGN.md, sections 2 and 4 | 2026-10-06 |
The lints pass: pnpm lint:all in Prototemplate and pnpm lint in gt-cloud. | gt-lints, review checklist | 2026-10-05 |
A landing or marketing page also clears the gt-landing-pages checklist (one rail, hatch spacers, heads, the shared Cta), checked at 390, 768, 1024, 1440 and 1920 in both themes.
| Bar | Owner | Read |
|---|---|---|
| Dither loops draw at their 30 fps budget by design. Every other animation meets the 60 fps bar in the Performance table. | gt-motion, section 2 (Durations) | 2026-10-05 |
| A language morph lasts longer than 0.5 s, so the glyphs visibly regroup. It finishes inside the time its locale holds the screen, such as one stop of the hero's locale belt. Kevin, 2026-08-05: "the morphings are still way too fast theyre like <0.5 sec". Later that day he called a slower morph "a lil too long". | This page | 2026-08-05 |
| A morph dissolves through glyphs at every width and never collapses into a fade. | gt-motion, section 5 | 2026-10-05 |
| A scroll story runs on one scrubbed dial. Each beat owns a scroll range with dwell, and scrolling back plays the story backward. | gt-motion, section 6 (Scroll stories); gt-landing-pages, Scroll stories | 2026-10-05 |
| Product pages have no entrance animation. | gt-motion, section 1 | 2026-10-05 |
| Reduced motion renders a designed still, and the resting markup reads without JavaScript. | gt-motion, section 1 | 2026-10-05 |
Scrolling is native (gt-ui/no-smooth-scroll). | gt-motion, section 1 | 2026-10-05 |
| Motion is verified with real pixels from an external browser, in both themes, under reduced motion, at 1440 and 390. | gt-motion, Verifying motion | 2026-10-05 |
| Bar | Owner | Read |
|---|---|---|
| Every crop comes from a registered capture of the real product. An illustrative mock in a set is described as a mock. | gt-graphics, What a visual is | 2026-10-05 |
Text in a blog visual measures 26px or more after the fit, and pnpm graphics:audit passes. | gt-graphics, Sizing for the blog column | 2026-10-05 |
| Renders are supersampled (shot 7680 wide and halved to 3840). Every visual with lines or small type is read at 1:1 as well as on the contact sheet. | gt-graphics, What a visual is and Procedure | 2026-10-05 |
| A diagram shows a relationship the text alone does not, drawn from the real artifact as one connected composition. | gt-diagrams, section 1 | 2026-10-05 |
| Numbers printed in a diagram match what the eye sees. Kevin, 2026-08-05: "yeah it just doesn tlook like +63%". | gt-aesthetic, review checklist | 2026-10-05 |
| Junctions are read at 2x crops in both themes, because the line auditor skips SVG. | gt-diagrams, section 9 | 2026-10-05 |
| An OpenGraph card has a composition of its own, apart from the header cover, set in the house type with the house lines and centered on its canvas. It is rendered and looked at after every change, and its size and themes follow gt-graphics (Sizing for the blog column). Kevin, 2026-09-17: "the tweet/blog header cover is the angled one and the opengraph is the exploded components". | This page | 2026-09-17 |
| Bar | Owner | Read |
|---|---|---|
| The hard rules hold: no em dashes, metaphors, contrast pairs, fragment rhythm, exclamation marks, rhetorical questions, signposts, eyebrows, marketing vocabulary or emojis. | gt-voice, Hard rules | 2026-10-05 |
| Every line passes the copy test: it states a number or a mechanism, and a line a competitor could use unchanged is cut. | gt-voice, Facts and public surfaces | 2026-10-05 |
| The humanizer pass scores 35 of 50 or higher across directness, rhythm, trust, authenticity and density. | gt-voice, The humanizer pass | 2026-10-05 |
| Heads are plain nouns in sentence case with no trailing period, and Title Case is kept for buttons. | gt-voice, Headings and casing | 2026-10-05 |
| Every sentence has a subject and a verb, the subject is short, and no aside interrupts a clause. | gt-voice, Sentence shape | 2026-10-05 |
| Public writing stands alone for a developer who has never heard of Kevin or GT. It defines the product once, and each section opens with the point a stranger can reuse. | gt-voice, Writing for strangers | 2026-10-05 |
| Nothing sensitive appears on a public surface: funding, revenue, headcount, customer counts or unannounced launches. | gt-voice, Facts and public surfaces | 2026-10-05 |
| Bar | Owner | Read |
|---|---|---|
| On a gt-cloud PR, Greptile reads 5/5 on the head commit. Kevin, 2026-09-13, after a merge at 4/5: "bruh u merged without addressingthis". | gt-ship, section 5 | 2026-10-05 |
| Every bot thread has a reply naming the fixing commit and is then resolved. | gt-ship, section 5 | 2026-10-05 |
The three required checks on gt-cloud main are present and green, and node skills/gt-ship/scripts/pr-bots.mjs <n> exits 0. | gt-ship, section 5 | 2026-10-05 |
node skills/gt-ship/scripts/pr-size.mjs was read and every large group is justified in the body. The diff holds no recorded fixtures, harnesses, review galleries or planning Markdown. | gt-ship, section 3 | 2026-10-05 |
The PR carries one concern under a conventional title, and a feat PR links a Linear issue. | gt-ship, section 3 | 2026-10-05 |
| The body is a two-sentence summary and What to look at, with before and after crops refreshed after every visible change. It is written in Kevin's own register (lowercase, first person), and the crops carry most of the explanation (Kevin, 2026-10-05). | gt-ship, section 4; gt-voice, Two registers | 2026-10-05 |
| Kevin merges the PR or says to merge it. | gt-ship, section 5 | 2026-10-05 |
A convergence loop iterates on quality until a bar holds: a page against its reference, a set of graphics against its best accepted item, a film cut against its brief. Kevin, 2026-07-30: "remember the bar is generaltranslation.com and resend.com. literally rereview and continuously score yourself until it look sright".
| Bar | Owner | Read |
|---|---|---|
| The brief names the reference and the bar before the first round. For a set, the bar is the best item Kevin has accepted. | gt-orchestration, section 4 | 2026-10-05 |
| The work and the reference are captured at the same geometry (viewport, theme, camera, crop) and placed side by side. | gt-orchestration, section 4 | 2026-10-05 |
| A fresh critic that did not build the work scores each view blind against the reference. It is told to be harsh, and it names what the reference does that the work does not. | gt-orchestration, section 4 | 2026-10-05 |
| Each view has a numeric bar wherever it can be scored, stated in the brief. The bar ends the loop; a cap on rounds is a budget stop, and the report lists the gaps still open. | gt-orchestration, section 4 | 2026-10-05 |
| One strong view never averages away a weak one, and the builder never scores its own work. | gt-orchestration, section 4 | 2026-10-05 |
| Refiners fix only the gaps the critic named. | gt-orchestration, section 4 | 2026-10-05 |
| Kevin's note outranks a critic's pass. An item he called wrong stays open until he says otherwise. | gt-orchestration, section 4 | 2026-10-05 |
| When scores stop rising, the method changes: measure the reference and rebuild, or change the idea. | gt-orchestration, section 4; gt-films, section 3 | 2026-10-05 |
| Bar | Owner | Read |
|---|---|---|
| Performance work leaves the look unchanged. An audit whose fix would change the design (contrast, tap target size, a dropped effect, a lower resolution) goes to Kevin with its measured gain. Kevin, 2026-08-05: "fix the lighthouse without changing anything aesthetically". | gt-performance, section 1 | 2026-10-05 |
| Animation and interaction run at 60 fps at full resolution. Loops start complete, and the first interaction never stalls. | gt-performance, section 1 | 2026-10-05 |
Frame time is measured before and after the change (node skills/gt-performance/scripts/frame-probe.mjs <url>) and reported in a table. | gt-performance, section 2 | 2026-10-05 |
| Every timing carries the machine's load average, and before and after runs go back to back under similar load, because parallel agents distort the numbers. | gt-performance, section 2 | 2026-10-05 |
| An engine with quality tiers picks its tier from measured frame cost. | gt-performance, section 3; gt-motion, section 6 | 2026-10-05 |
| Lighthouse is read from a production build in a clean profile, or from production after the merge, and the PR carries the real report. | gt-performance, section 6 | 2026-10-05 |
| A budget lives in the probe that measures it and fails like a lint, and a red budget blocks the change. | gt-performance, section 8 | 2026-10-05 |
| Bar | Owner | Read |
|---|---|---|
| Kevin reads the scripts before the build, which waits for his answer. He picks a new narrator from auditions. | gt-films, sections 3 and 7 | 2026-10-05 |
| A critic watches each draft frame by frame (every 0.5 s, and every 0.25 s around each cut and major move) and reports every defect with its time and a fix. | gt-films, section 9 | 2026-10-05 |
node skills/gt-films/scripts/measure-render.mjs passes on the render (-17 to -15 LUFS, true peak under -1 dBTP, 1920 by 1080 at 60 fps), and hyperframes check ends with 0 errors. | gt-films, review checklist | 2026-10-05 |
| A non-English take has a native listener's sign-off. | gt-films, section 7 | 2026-10-05 |
Kevin set these bars for benchmark charts on 2026-07-30 and 2026-09-30. The wiki's dataviz skill holds the general method for form, color and marks.
| Bar | Owner | Read |
|---|---|---|
| The win is obvious at a glance. | This page | 2026-07-30 |
| Compared series are stacked or overlaid so the margin reads exactly, and the bars in one set share one style. | This page | 2026-07-30 |
| The method is stated beside the chart: what was measured, how, and on what. | This page | 2026-07-30 |
| A metric that shows cost savings is prominent. | This page | 2026-07-30 |
| Benchmarks are run fresh for the chart that ships. | This page | 2026-09-30 |
| The chart leads with the most impressive true metric. Kevin, 2026-09-30: "why just tokens? show our speed diagram too thats more impressive". | This page | 2026-09-30 |
| Marks match their values, and every number agrees with each other place it appears. | gt-aesthetic, review checklist | 2026-10-05 |
Kevin set these bars for a repository's own documents between 2026-08-02 and 2026-09-30.
| Bar | Owner | Read |
|---|---|---|
| The README holds the shipped product's instructions and stats in a concise form, with the OpenGraph image and a demo GIF at the top and screenshots of what it does. Kevin, 2026-09-30: "youre treating the readme as like a log of work instead of a concise summary". | This page | 2026-09-30 |
| The repository's description and topics are set. | This page | 2026-08-04 |
A LICENSE file states the terms. Prototemplate's license reserves all rights to General Translation, Inc., and third-party fonts, icons and adapted skills keep their own licenses. | prototemplate, section 10; LICENSE | 2026-10-05 |
AGENTS.md explains the repository's layout and philosophy, SKILL.md explains how to use the tool and everything it does, and both match the code. | This page | 2026-08-02 |
| Prose that has drifted from the code is corrected in the change that finds it. | prototemplate, section 1 | 2026-10-05 |
Work is ready to show when its artifact's table holds on localhost or in a file Kevin can open, and the report carries before and after crops of each changed region in both themes (gt-aesthetic, Showing the work). It is ready to ship when the pull request table holds as well. It is done when the rows below hold.
| Bar | Owner | Read |
|---|---|---|
| A visual fix is measured on the render at the spot Kevin flagged (computed values, 2x or 4x crops, pixel probes), in every state of a moving element. | gt-verify, section 1 | 2026-10-05 |
| The real gesture and the real flow work in the running app, for every category of item the change covers. Any shortcut is named and then replaced by the real gesture. | gt-verify, section 2 | 2026-10-05 |
| The stress matrix, viewports, themes, browsers and edge counts that apply were walked. | gt-verify, sections 3 to 5 | 2026-10-05 |
| Items that were right before the change are still right. | gt-verify, section 6 | 2026-10-05 |
| Every member of the defect's class was found, fixed and checked. | gt-verify, section 8 | 2026-10-05 |
Shipped work is live: every lane's commits are on main, production's commit contains them, the live page carries that deployment's stamp, and the OG card is read live. For Prototemplate, the dpl stamp on www equals the team deployment. | gt-verify, section 7; gt-ship, section 8 | 2026-10-05 |
| The report says how each item was verified and names what was not, with the reason. | gt-verify, section 12 | 2026-10-05 |
skills/gt-aesthetic/SKILL.md and references/verdicts.md, skills/gt-verify/SKILL.md, skills/gt-orchestration/SKILL.md, skills/gt-performance/SKILL.md, skills/gt-landing-pages/SKILL.md, skills/gt-lints/SKILL.md, skills/gt-motion/SKILL.md, skills/gt-graphics/SKILL.md, skills/gt-diagrams/SKILL.md, skills/gt-voice/SKILL.md, skills/gt-ship/SKILL.md, skills/gt-films/SKILL.md, skills/gt-dither/SKILL.md, skills/gt-reporting/SKILL.md, skills/prototemplate/SKILL.md.DESIGN.md section 2, deck/DECK-GRAMMAR.md, docs/SHIP-LOOP.md section 2, scripts/check/pagecheck/README.md, LICENSE.AGENTS.md and SKILL.md), 2026-08-04 (two-line heads, repository topics), 2026-08-05 (morph length, numbers in diagrams, Lighthouse with no visual change), 2026-08-06 (the Dossier), 2026-08-11 (the diagram restart), 2026-08-17 (the second pass), 2026-09-13 (Greptile 5/5), 2026-09-17 (the OpenGraph card's own composition), 2026-09-30 (chart metric, README), 2026-10-05 (the page heads, PR bodies that let the crops explain), 2026-10-07 (the shipped site as the site's reference).Kevin Liu runs many agent sessions on one machine at the same time for his General Translation (GT) work: named Claude Code sessions, Codex sessions, and forks of both. Codex is OpenAI's coding agent, and Kevin runs it beside Claude Code. Inside Claude Code, the ListAgents tool lists the other Claude sessions on the machine by name, and the SendMessage tool delivers a message to one of them. This page holds the practices that let every session add to the others' work and undo none of it.
The single rules live in skills, and this page points to them. gt-ship sections 1, 2 and 8 hold lanes, worktrees, staging and the shared Prototemplate checkout. gt-orchestration holds a session's own subagents, resumption (section 6) and handoffs at a hard stop (section 8). The prototemplate skill section 8 holds the lane map of the shared Prototemplate tree. The glossary defines lane, session, forked session, handoff and relay note. When a skill and this page disagree, the skill wins.
One session owns one lane. ListAgents prints the live list of sessions, and the table lists the GT lanes as of 2026-10-05.
| Session | Owns | Where it works |
|---|---|---|
| Prototemplate | The site at prototemplate.com, its viewer shell, the deck build, the skills and the docs | The shared checkout ($PROTOTEMPLATE), with the dev server at http://localhost:3005 |
| Videos | The films, their kit and MOTION.md | Prototemplate motion/, which stays untracked. Its Prototemplate commits carry only film and poster files in public/media. |
| Glyph Map | The /world page on generaltranslation.com | gt-cloud k/language-map in its own worktree, port 3031 |
| New Onboarding and Dashboard | The dashboard sign-in and onboarding PRs, and in Prototemplate the mood slides in deck/, src/components/plate, src/app/craft and public/brand/mood | Its own gt-cloud worktrees. It pushes Prototemplate main from its own worktree. |
| Lottie | The blog's Lottie translation figure | gt-cloud #5068, k/blog-lottie-translation in its own worktree, port 3024 |
| docs redesign | The docs site on generaltranslation.com | Its own gt-cloud worktrees |
Kevin's personal projects run in sessions and repositories of their own. They share the machine with the GT lanes (section 10) and nothing else.
deck/. The session about to change a shared folder messages the other first (section 8).Kevin, 2026-10-03: "that videos one should be making them, and this one is just for prototemplate work".
Kevin forks sessions often. Ten gt-cloud sessions active between August and October descend from one session started on 2026-07-30. Videos was forked from New Onboarding and Dashboard on 2026-10-01, Glyph Map from Videos on 2026-10-02, and the Prototemplate session from New Onboarding and Dashboard on 2026-10-03. A fork starts with its parent's whole message history, and every inherited message reads as if it were addressed to the fork.
Kevin, 2026-10-02: "wait another agent is workign on the motion and videos. just focus on the nnew page".
git status --short, git log --oneline -15 on its branch and on origin/main, git worktree list, the lane's notes (memory notes, HANDOFF.md, the round's spec) and ListAgents.~/.claude/projects/<project>/<session>.jsonl), an inherited message keeps the parent's sessionId. One message of 2026-07-31 sits in eleven gt-cloud transcripts with a single originating sessionId. A count of Kevin's messages across transcripts dedupes on sessionId and timestamp.Kevin sometimes sends one instruction to several sessions within seconds. On 2026-08-06 "commit and push everything to prototemplate, our branches, and 4213" reached four gt-cloud sessions within 25 seconds. Two of them went after the same three targets. One of the two had its Prototemplate build collide with a concurrent build. The other later saw a worktree directory vanish between two commands, because a parallel session was porting into it.
git fetch, then git log --oneline HEAD..origin/<branch> or gh pr view <n> --json headRefOid). They report the sha they found. When ownership is unclear, they ask the other session by SendMessage before pushing.gt-ship sections 1, 2 and 8 and prototemplate section 8 hold the rules for a shared checkout. They give motion/ to Videos, require explicit paths and a pathspec commit, check the branch before every commit, keep the primary gt-cloud checkout on its old branch, and leave the Prototemplate dev server on 3005 and its .next alone.
.claude folder, and on 2026-10-02 a preview written there had to be moved out.docs/SHIP-LOOP.md section 0). Kevin, 2026-07-31: "dont worry about the uncommited work. only worry about localhost3006/ page".tsc failed on a parallel edit that used Code2 before importing it. By the time the session looked, the other session had added the import, and the rerun passed.pnpm-workspace.yaml held a placeholder line that made pnpm install fail.v0-pages.css at higher specificity, and the fix changed that one rule to blue. On 2026-09-29 Kevin asked for the earlier glyphs back, and the fix restored only the rain layer from the earlier head and kept the other session's gallery console.gt-ship section 1 records the case of the CLI login callback page (2026-09-30).Kevin, 2026-07-30: "let codex do its own thing, but branch off again".
codex/<topic> is renamed k/<topic> before it becomes a gt-cloud PR (2026-08-27).git -C <worktree> branch --show-current and git -C <worktree> log --oneline -1 for each worktree involved. The answer on 2026-07-30 named both worktrees, both branches, both ports and the commit both branches started from.origin/main and adds to the other agent's work. Kevin ruled on 2026-09-23 for a main that both agents push. Each agent keeps absorbing the other's pushes by rebase, every push to main has run the core test suite, and the two agents share main without separate deploy windows ("WE MERGE").git diff --stat <base> origin/main with your own change. For every file both agents touched, keep the incoming behaviour and re-apply yours on top. A merge without conflicts can still undo a feature through a whole-file replacement. 3. Read git diff origin/main HEAD in full, removals included, as the change about to be published. 4. Run the gates on the integrated commit. A green run on the branch before the merge says nothing about the merge. 5. Push without force. If the push is rejected, fetch, integrate and run the gates again.In GT's repositories, gt-cloud main takes only squash merges through PRs with three required checks (gt-ship section 5), and Prototemplate's gates and deploy checks are in gt-ship section 8.
origin/main, list the week: git log origin/main --since='7 days ago' --format='%h %ad %s', git worktree list and git branch -r. - Check every branch that looks unmerged for patch equivalence with git cherry -v origin/main <branch> and git range-diff. A squash or a rebase changes commit ids and keeps the work. - Read each merge commit's conflict resolutions with git show --remerge-diff <sha>. - For merged PRs, measure how many of their added lines survive on main and trace every removal to a stated follow-up. On 2026-09-08 Kevin thought the routing PR #4359 had been reverted. The gt-cloud audit found it open and unmerged with every commit intact, and it merged the next day. - Leave the other agent's unfinished checkout as it is and list it in the report.A relay note is written by one agent for another, usually Claude for Codex or Codex for Claude, and Kevin pastes it as is. It holds:
To continue another agent's uncommitted work, snapshot it as a commit in your own worktree and leave theirs as it was. The relay of 2026-09-26 said so in its first line. The receiver finds the other agent's open work and finishes it. Kevin, 2026-09-25: "finish any of codexs open work as well". The receiver checks the note against git and the files before trusting it. A handoff at a hard stop follows gt-orchestration section 8.
Kevin, 2026-10-05: "give me the prompts to send to the agents in the exact correct convos (give me exactly where to do this)".
9690311..f8dfa8b), asks the receiver to fetch and rebase, and states any new gate (pnpm build now runs the picture lint first). New Onboarding and Dashboard's messages to Prototemplate on 2026-10-05 took this form./motion is re-rendered, so the public copy and its credits are refreshed.Plans, lane specs, ask ledgers and outputs live in files, so a session that restarts, compacts or hears "continue" resumes from disk without a new brief. The procedure is gt-orchestration section 6. A resumed session reads the shared state again (ListAgents, git, the lane's notes) before it acts, because the other sessions kept working while it was stopped.
gt-orchestration section 2). A verify lane starts its own server on a free port and stops it when done. The Browser pane in one Claude session cannot reach a dev server that another chat started, so a session looks at that server through a Playwright capture or starts a server of its own. A conflict over a port goes to the session that owns the server, or to Kevin.prototemplate section 9). Where a repository has a capture lock or queue, a session takes it for the run, releases it at the end, and never holds it while idle.gt-orchestration section 2 holds the rule for timing checks under load: record uptime beside the reading and rerun a red check once the load drops. While the machine is busy, prefer measures that load cannot change: counts, bytes and deltas.gt-ship section 1 gives the checks to run first and the command for removing many at once.git status, recent commits on the branch and on origin/main, and the lane's notes were read before acting.gt-ship section 2).uptime sits beside timings, and scratch worktrees are removed.skills/gt-ship/SKILL.md sections 1, 2, 5 and 8; skills/prototemplate/SKILL.md sections 8 and 9; skills/gt-orchestration/SKILL.md sections 2, 6 and 8; docs/SHIP-LOOP.md section 0; docs/handbook/glossary.md (working terms).session-lanes-prototemplate, ship-loop-hard-gates, scratch-worktree-disk, lost-work-audit-2026-09, gt-motion-films, world-language-map, blog-lottie-figure, signin-field-transition and landing-hero-agent-button.~/.claude/projects/: the fork lineage read from each transcript's inherited sessionId and title records; the 2026-08-06 instruction and the four sessions' responses; the 2026-10-03 handover to Videos; the 2026-09-29 and 2026-09-30 messages between the sign-in sessions; the 2026-10-05 messages from New Onboarding and Dashboard and Videos; and the desktop app's notice that another chat's dev server is out of the Browser pane's reach (2026-09-29). The Codex sessions under ~/.codex/sessions/2026/09/ hold the shared-main audit of 2026-09-22 and the relay of 2026-09-26.This page is for agents and people who write about General Translation (GT), design for it or change its products. It states what GT sells and to whom, how the products fit together, the facts copy must get right, and where each part lives in the repositories. Each fact carries the date it was read or ruled. Everything on the page is safe to publish: it names no customer, states no business figures and leaves internal data-model details to gt-cloud's own documents.
Detail stays with the documents that own it:
AGENTS.md (which CLAUDE.md links to) and its .agents/skills hold the monorepo file map and the engineering rules, such as audit-events and server-security.$GT_CLOUD is a gt-cloud checkout at origin/main. gt-cloud facts below were read at e17fce499 on 2026-10-05.
The home page hero says GT "builds full-stack localization for apps, docs, and websites" (read 2026-10-05). GT builds every layer of that stack itself:
Kevin, 2026-07-30: "the whole point is that were a full stack localization, end to end".
| Product | What it does | Where it lives |
|---|---|---|
The gt libraries | gt-next, gt-react, gt-tanstack-start and gt-react-native (each 11.4.9 on npm, 2026-10-05), gt-vue and gt-node render translations and format locale-sensitive values in the app. gt-next also detects the locale and routes by it. | generaltranslation/gt, packages/ |
| The Python SDKs | gt-flask and gt-fastapi on PyPI, built on the generaltranslation core package. BRAND.md's naming table writes the family as gt-python, which is the repository name; no package of that name exists (PyPI, 2026-10-05). | generaltranslation/gt-python |
The gt CLI | Sets up a project, signs in, translates files and calls the API (gt 2.25.1 on npm, 2026-10-05). Section 7 has the commands. | generaltranslation/gt, packages/cli |
| The platform and dashboard | Projects, Context Groups, the translation editor, review and approval, version branching, annotations and audit logs at dash.generaltranslation.com. Review works over the web workspace, the API and the CLI. | $GT_CLOUD/apps/dashboard, apps/api |
| The translation API | Context-aware translation of text, files and components, published as /openapi.yaml. | $GT_CLOUD/apps/api |
| The translation CDN | Serves pre-generated translations from a global, low-latency CDN, and pushes over-the-air updates without a redeploy of the app. | $GT_CLOUD/apps/edge (Cloudflare Workers) |
| Runtime translation | Translates user-generated content on demand with the project's context. In Next.js it is <Tx> and tx() from gt-next/server, built on GTRuntime in the core library. | generaltranslation/gt, packages/core and packages/next |
| Locadex | A hosted agent. It connects to GitHub and to content sources (Google Drive, Figma, a CMS), scans the repository, internationalizes the code, generates translations and opens pull requests. Its automations keep the app localized after the first pass. | $GT_CLOUD/apps/locadex, packages/locadex-core |
| Integrations | Mintlify, Sanity (gt-sanity), Storyblok, Google Drive and rrweb (gt-rrweb), the GitHub Action, and the MCP server (@generaltranslation/mcp). | generaltranslation/gt (Sanity, rrweb, MCP), $GT_CLOUD/packages/integrations (hosted connectors such as Google Drive and Storyblok), generaltranslation/translate (the Action) |
The React libraries share one API. <T> wraps a block of static JSX, and <Var>, <Num>, <Currency> and <DateTime> mark its dynamic parts. gt() translates a string, msg() marks strings in a data array, and <Tx> and tx() translate content at runtime in server code. The usage rules, including const gt = useGT(); and await getGT() in async server code, are in gt-components and gt-cloud's AGENTS.md.
Context reaches a translation through three layers: the Organization, then the Project, then the Component (Kevin's outline of 2026-07-30; the dashboard guide Defining context for translations, read 2026-10-05). Each layer is inherited by the one below it.
| Layer | What sets context there |
|---|---|
| Organization | Context Groups live at the Organization level. A Context Group pairs a Glossary with Custom Prompts. |
| Project | A project applies one or more Context Groups in priority order. The top group wins where two groups overlap, and newly generated terms are saved to it. |
| Component | <T context="..."> sets context for one block. gt() and getGT() take a $context option. $context on <T> is a deprecated alias. |
<T context="notification popup, not bread">Click the toast to dismiss</T>.org:context:read and org:context:write) and its MCP tools automate groups, terms and prompts.Plan facts come only from the public pricing page, https://generaltranslation.com/pricing, quoted as it read on 2026-10-10. Plan rules the page does not state, such as what a new account receives, are internal and stay in gt-cloud's gt-dashboard skill.
The comparison table follows the cards directly (Kevin, 2026-08-15), and the page's diagram sits below the plans. The money format on pricing pages is gt-landing-pages' whole-dollar rule.
Kevin set the four enterprise pillars on 2026-08-15, and the enterprise page carries them (read 2026-10-05). His list named the third pillar Forward-deployed engineers, and the live page heads it Forward-Deployed Support, the name copy uses.
| Pillar | Copy |
|---|---|
| Security and governance | SSO, SOC 2, ISO 27001, audit logs, and custom roles. The pricing table adds SSO over SAML and OIDC, SOC 2 Type II and RBAC with custom permissions. |
| Enterprise platform | Share translation context, glossaries, and custom prompts across every project and content source in your company. |
| Forward-deployed support | Dedicated FDE hours with localization engineers to set up your system and bring localization to production. |
| Custom workflows | Reliable, scalable translation workflows across any file format or framework. Custom integrations, webhooks, and tailored automation. |
| Topic | What copy says | Source |
|---|---|---|
| Layout per language | "Dynamic layout for each language". GT rearranges components for a language, such as right-to-left order for Arabic. The components themselves stay unchanged. | Kevin, 2026-08-06: "technically we dont alter components, but can rearrange them dynamically like for arabic" |
| Review surfaces | "Review and approve with your team, over web, API, or CLI." Copy names all three surfaces, because enterprises judge vendors on agent readiness. | Kevin, 2026-08-06: "from one surface implies that we're forcing you to use our dashboard" |
| Formatting | "Numbers, currencies, dates, plurals, and more", with no "edge cases handled" lead. | Kevin, 2026-08-06 |
| Delivery | "Served using a global, low-latency translation CDN." and "Push over-the-air updates without redeploying your app." Translations can also ship with the code, so the home page's delivery band reads "Served from local files or the edge" (read 2026-10-05). Copy that describes delivery in general names both paths. | Kevin, 2026-08-15, for the enterprise page's delivery panel |
| Enterprise voice | "Every company's localization needs are different. General Translation adapts to your existing stack, workflows, and review process." The customer uses the platform with its own team or alongside GT's forward-deployed engineers. A line that implies GT's team is better than the customer's is cut, and gt-voice holds the register. | Kevin, 2026-08-15 |
| Localized heroes | Each locale's hero line is idiomatic and carries the exact meaning in that language and culture. A locale route such as /ja opens with its hero sentence and diagram in that locale. | Kevin, 2026-08-08 |
| Geography | Content about places is organized by language. It names no country or territory, draws no borders, takes its facts from CLDR, and treats disputed areas through one policy. | Kevin, 2026-10-03: "avoid political sensitivity without making it look forced" |
| Infrastructure diagrams | Edge nodes carry real cloud region names such as us-east-1, shortened where a label would clip. | Kevin, 2026-08-06 |
| Names | Product tokens keep their exact form (gt, gt-next, gt login), Locadex is never translated, and the naming table is BRAND.md section 1. | BRAND.md, gt-voice |
| Public facts | Customer names come only from BRAND.md section 9. No surface states funding, revenue, headcount or the timing of an unannounced launch. | gt-brand section 8 |
The CLI facts below come from the start prompt served at /agent-prompt.md (read 2026-10-05) and from the CLI source on generaltranslation/gt origin/main. A coding agent ran the prompt end to end on a fresh Next.js app on 2026-09-25 with gt 2.22.3 and again on 2026-09-30 with gt 2.23.1. The published CLI was 2.25.1 on 2026-10-05.
gt login signs the CLI in with the person's GT account. It opens GT's sign-in page in the browser and shows a confirmation page when sign-in completes. gt login --no-browser prints a sign-in URL and a code to use on any device, and an SSH session takes that path by default. gt whoami reports the signed-in account.AGENTS.md of generaltranslation/content.gt translate --dry-run shows the scope, and gt translate translates it. With translations stored in the repository, the run writes public/_gt/<locale>.json, gt-lock.json and a _versionId in gt.config.json.gt init and gt configure ask any unanswered question through an interactive prompt and wait with no timeout. An agent passes --no-interactive (or --json, which implies it) and a flag for every question. A missing answer then exits 1, lists the flags still needed and changes no file. Kevin asked on 2026-10-01 that agents always run the CLI this way.--version flag. npm view gt version gives the published version, and the translate banner prints the installed one. gt auth is not a command in current releases..env.development.local, which next dev loads and next build does not. A production next build refuses to run with a development key present.The docs serve agents directly (gt-cloud #4499, merged 2026-09-10). gt-website's references/docs.md holds the routes behind them.
/llms.txt is the curated entry point, /llms-index.txt lists every page, and /openapi.yaml is the API specification. /llms-full.txt, scoped /docs/<scope>/llms.txt files, /AGENTS.md and /sitemap.md sit beside them..mdx or .md to a docs page URL returns the page's raw Markdown twin.docs/en-US/overview/for-coding-agents.mdx in the content repository. The same text is served raw at /agent-prompt.md. After any change to the prompt, the CLI or the quickstarts, the prompt is run end to end by a coding agent on a fresh app. Kevin, 2026-09-30: "Agent experience (AX) is a huge deal for us".The start prompt sets these onboarding rules for an agent (read 2026-10-05):
gt login. The agent never asks for a pasted API key, a password or OAuth tokens, and it writes new keys to ignored files without printing them.| Repository | Visibility | What it holds |
|---|---|---|
generaltranslation/gt-cloud | private | The turbo and pnpm monorepo: the dashboard, landing, API, workers, AI proxy, edge, Locadex and admin apps, and the shared packages (packages/ui, db, node, settings and others). |
generaltranslation/content | public | Docs, blog posts and devlogs, mounted at apps/landing/content as a submodule. Every landing deploy checks out the tip of content main ($GT_CLOUD/scripts/deploy-landing.sh), and a merge to content main triggers a deploy. |
generaltranslation/legal | public | The legal pages, a pinned shallow submodule at apps/landing/legal. |
generaltranslation/gt | public | The CLI, the JavaScript libraries, the core library, the compiler, the MCP server and the Sanity and rrweb plugins. |
generaltranslation/gt-python | public | The Python SDKs. |
generaltranslation/translate | public | The GitHub Action. |
Kevin-Liu-01/Prototemplate | public | The design hub and public reference behind prototemplate.com: BRAND.md, DESIGN.md, the deck, the skills and this handbook. |
File-level maps stay with their owners: gt-cloud's AGENTS.md and .agents/skills (gt-landing, gt-dashboard, gt-api, gt-admin), gt-website for the landing and gt-brand for the identity files.
gt-website owns the site's routing. Its references/docs.md holds the unsupported-locale redirects, the docs near-miss ladder and suggestion page (gt-cloud #4359), the markdown twins and the step order in src/proxy.ts. The rules below are not in that reference. Each was read on gt-cloud's origin/main and checked on production on 2026-10-05.
apps/landing/staging.gt.config.json lists es only, and gt.config.json keeps the production roster (es, fr, zh, ja, it, ru and en-GB beside en-US). Kevin, 2026-08-08: "only es for staging".noindex, nofollow by design (gt-cloud #4359). A real 404 needs the not-found boundary, and under the force-static root layout that boundary never sees the requested slug. The blog checks frontmatter aliases and close matches before it renders its own suggestion page.Every address below answered HTTP 200 on 2026-10-10. BRAND.md section 8 says how the identity applies to each kind of surface.
| Surface | Address |
|---|---|
| Home page | https://generaltranslation.com |
| Pricing and usage rates | https://generaltranslation.com/pricing, https://generaltranslation.com/pricing/usage |
| Enterprise and its contact form | https://generaltranslation.com/enterprise, https://generaltranslation.com/enterprise/contact |
| Careers and contact | https://generaltranslation.com/careers, https://generaltranslation.com/contact |
| Documentation | https://generaltranslation.com/docs, with https://generaltranslation.com/llms.txt and https://generaltranslation.com/agent-prompt.md for agents (section 7) |
| Knowledge base | https://generaltranslation.com/kb |
| Blog and changelog | https://generaltranslation.com/blog |
| Supported locales | https://generaltranslation.com/supported-locales |
| Localization report card | https://generaltranslation.com/report-card |
| Legal pages | https://generaltranslation.com/legal |
| Dashboard sign-in | https://dash.generaltranslation.com |
| Social | https://x.com/generaltxn, https://www.linkedin.com/company/generaltranslation/ |
| Open source | https://github.com/generaltranslation |
| Design hub (this repository) | https://www.prototemplate.com, with /brand, /docs, /handbook, /skills, /deck, /motion, /graphics, /marks, /blog and /d/production |
| Companion tooling | https://glyphfield.com, the shader and animation studio behind several graphics and films |
references/docs.md and references/pages.md; gt-voice; gt-landing-pages; gt-components.AGENTS.md, .gitmodules, apps/landing/gt.config.json, apps/landing/staging.gt.config.json, apps/landing/src/app/[locale]/docs/[variant]/[...slug]/page.tsx, apps/landing/src/app/[locale]/blog/[slug]/page.tsx, apps/landing/src/lib/legal.ts, apps/landing/src/components/landing/sections/global/Global.tsx, apps/landing/src/components/pages/enterprise/services-landing/GovernedExplorer.tsx, scripts/deploy-landing.sh, .agents/skills/gt-landing/SKILL.md; pull requests #4359, #4499 and #5049.generaltranslation/gt at origin/main a16ae03c6 (2026-10-05): packages/, packages/cli/src/cli/base.ts, packages/cli/src/auth/__tests__/oauth.test.ts, packages/cli/src/cli/__tests__/initOnboarding.test.ts, packages/core/src/runtime.ts, packages/next/src/server.ts, packages/next/src/index.types.ts.generaltranslation/content at origin/main (2026-10-05): AGENTS.md, Public authentication boundary./pricing (section 5) and the addresses in section 10, each fetched with curl. Read 2026-10-05: the home page, /en-US/pricing, /en-US/enterprise, /en-US/careers, /ja, /llms.txt, /llms-index.txt, /agent-prompt.md, /openapi.yaml and the docs guide Defining context for translations.generaltranslation GitHub organization, read 2026-10-05.This glossary defines the terms that appear in Kevin Liu's messages, in the General Translation (GT) skills and in the code of gt-cloud and Prototemplate. Each entry defines its term in one to three sentences and names the file, skill or document that owns it. The owner holds the full rule, so where the two disagree the owner is right and this page needs a fix. The vocabulary covers 2026-07-20 to 2026-10-05, and the retired terms in the last section point to decisions.md.
Paths without a prefix are in a Prototemplate checkout. A path marked gt-cloud: is in gt-cloud, gt: marks one in the open-source gt repository and content: one in the content repository.
| Term | Meaning | Where it lives |
|---|---|---|
| General Translation (GT) | General Translation, Inc. builds the full stack for localization: open-source i18n libraries, context-aware translation APIs, a platform for review and delivery, and the Locadex agent. GT is the short form and the mark. | BRAND.md sections 1 and 2; gt-product-map.md section 1 |
| gt (the libraries) | GT publishes its open-source i18n libraries from one public monorepo. On npm the package named gt is the CLI, and generaltranslation is the core library the others build on. | gt:packages/; BRAND.md section 1 |
| gt-next and its siblings | These framework packages render translations and format locale-sensitive values in an app: gt-next, gt-react, gt-tanstack-start, gt-react-native, gt-vue and gt-node, with gt-flask and gt-fastapi for Python. gt-cloud's own apps translate their UI with gt-next. | gt:packages/next and its neighbours; the Python SDKs in generaltranslation/gt-python; gt-product-map.md section 3 |
<T> | The <T> component marks a block of JSX for translation. Wrap the largest static block once, never nest it, pass dynamic parts through Var, Num, Currency, DateTime, Plural and Branch, and guide one block with its context prop. | gt-components references/react.md; gt-cloud CLAUDE.md |
gt() | The gt() function translates a string. It covers the user-facing props that <T> does not reach, such as a placeholder, a title or an aria-label. | gt-components references/react.md |
msg() | The msg() function marks the strings of a data array where the array is declared. The component that renders the array reads them through useMessages() or getMessages(). | gt-components references/react.md |
useGT | The client hook returns the gt function itself, so the line is always const gt = useGT(); and never const { gt } = useGT(). | gt-cloud CLAUDE.md |
getGT | Async server components and generateMetadata get the function with const gt = await getGT();, imported from gt-next/server. | gt-cloud CLAUDE.md |
| The gt CLI | The CLI signs in with gt login through GT's sign-in page and a 127.0.0.1 loopback callback, and gt translate translates a project after a --dry-run. Because gt init and gt configure wait on interactive prompts with no timeout, an agent passes --no-interactive and a flag for every question. | gt:packages/cli; gt-product-map.md section 7 |
| Locadex | Locadex is GT's hosted AI agent. It internationalizes a codebase, generates translations, opens pull requests and keeps the app localized afterwards. Its name is never translated, and drawings seat its real mark as an alpha mask (the gt-ui rule no-gif-mark refuses a gif). | gt-cloud:apps/locadex, public/brand/locadex-mark.svg; gt-brand; gt-product-map.md section 3 |
| Context Group | A Context Group is a reusable collection of translation context kept at the organization level: a Glossary and the style prompts. A project applies groups in priority order, and context then narrows from the organization to the project to the component (<T context="...">). | gt-cloud:apps/dashboard, route org/[orgId]/context/[groupId]; gt-product-map.md section 4 |
| Glossary | The Glossary is the terminology half of a Context Group. It lists product and brand names, feature names, technical terms and phrases that stay untranslated, and its standing example is that Locadex is never translated. | the group's glossary route; gt-product-map.md section 4 |
| Directives | Directives was Kevin's July name for the style half of a Context Group: tone, audience, formality, conventions and formatting. The dashboard, the API and the pricing page call it Custom Prompts, copy uses that name, and the code keeps directives (DirectivesEditor). | the group's custom-prompts route; gt-product-map.md section 4 |
| The dashboard | The dashboard is GT's product app at dash.generaltranslation.com. It holds projects, Context Groups, the translation editor, review, billing, and the sign-in and onboarding pages on the plate frame, and its product surfaces are judged against the brand deck. | gt-cloud:apps/dashboard; gt-cloud .agents/skills/gt-dashboard; gt-aesthetic section 1 |
| The translation CDN | The translation CDN is an edge service on Cloudflare Workers that serves translations. Through it a team pushes translation updates over the air without redeploying its app. | gt-cloud:apps/edge; gt-product-map.md section 3 |
| Setup for Agents | Setup for Agents is the second button in the landing hero (gt-cloud #5049). It copies the start prompt that takes a coding agent from an empty project to a first translation, and every change to the prompt, the CLI or the quickstarts is proven by handing the prompt unchanged to a fresh agent on a fresh app. | content:docs/en-US/overview/for-coding-agents.mdx, served at /agent-prompt.md; gt-website section 6 |
| The report card | The report card is a public page that grades how localized any site is on six checks: hreflang, language declaration, routing, metadata, content and charset. Prototemplate's copy is the /report-card page of the shipped site. | generaltranslation.com/report-card (gt-cloud:apps/landing/src/app/[locale]/(home)/report-card, apps/landing/src/lib/report-card/); /d/production/report-card, src/components/try/, src/lib/try/ |
| Term | Meaning | Where it lives |
|---|---|---|
| gt-cloud | The gt-cloud repository is GT's private product monorepo on turbo and pnpm. It holds the dashboard, the landing site with the docs and blog, the API, workers, the AI proxy, the edge CDN, Locadex, the admin app and the shared packages, and it squash-merges small PRs from k/<topic> branches once three required checks pass and the review bots are answered. | github.com/generaltranslation/gt-cloud; gt-cloud AGENTS.md; gt-ship section 5 |
| The gt repository | The public monorepo generaltranslation/gt holds the CLI, the JavaScript libraries, the core library, the compiler and the MCP server. CLI changes such as the login callback page land there. | github.com/generaltranslation/gt; gt-product-map.md section 8 |
| The content repo | The public generaltranslation/content repository holds the docs, blog posts and authors, and gt-cloud mounts it as the apps/landing/content submodule. Every landing deploy checks out the tip of content main, so merging a content PR publishes it, and when a post needs new components, images or redirects the gt-cloud side merges first. | gt-website sections 4 and 5; gt-product-map.md section 8 |
| Prototemplate | Prototemplate is Kevin's public hub and wiki for GT work. Its Next.js site, served at www.prototemplate.com, prototemplate.vercel.app and prototemplate.kevinliu.studio, holds the brand book, the design canon, the deck, the design lab, the marks, the films, the graphics, the skills and this handbook. Its main holds the primary copy of the canon, all rights are reserved to General Translation, and its dev server runs on localhost:3005. | github.com/Kevin-Liu-01/Prototemplate; prototemplate section 1 |
| The deck | The deck is the GT brand deck: 93 slides built from deck/ into one self-contained viewer that /deck frames. It is the reference for every product surface, and the Prototemplate viewer shell was modeled on its viewer (2026-09-08). | deck/, deck/DECK-GRAMMAR.md, pnpm build:deck; gt-deck |
| The Dossier | The Dossier (singularity-dossier) is the direction the shipped GT site grew from, and it stays in the gallery. Kevin called it complete on 2026-08-06 ("dossier is indeed our completed version and treat it as such"), and it was the reference for every marketing page until 2026-10-07, when he ruled "fix the dossier references". Since then the shipped generaltranslation.com has been the site's reference (decisions.md). | /d/singularity-dossier and its /enterprise; BRAND.md section 8; gt-aesthetic section 1 |
| Toolchain | Toolchain is the direction that served as the single source of truth for the forks from July. It set one ruled column, bento rows, isometric line diagrams and the tc-* vocabulary that the gt-cloud landing engine still uses, and its enterprise and pricing pages are literal specs, copy included. | src/app/d/toolchain/, /d/toolchain; ARCHITECTURE.md "The SSOT rule" |
| A direction | A direction is one exploration of the GT site's design, served at /d/<slug> and registered in DIRECTIONS, where four entries are full sites (the Dossier, Orbit, Signal and production). In July and August Kevin named directions by their label numbers, and the labels were renumbered more than once (Toolchain was 10 then and is 01 now). | src/lib/directions.ts; /directions/<slug>; retired ones at /archive/<slug>; gt-explorations section 3 |
| A fork | A fork is a direction built on Toolchain's sections and diagrams, imported directly and re-skinned through its own root class (.lensgate-root, .terminusboard-root). Fork work never edits Toolchain's files, and a forked session is a different thing, defined under Working terms. | ARCHITECTURE.md "The SSOT rule" |
| The presenter | The presenter at /present is a full-screen tool built on 2026-07-30 to review the directions. It runs an intro, the case for the redesign, a craft gallery, a live viewer of the first 16 prototypes and a scoreboard. | src/app/present/ |
| /d/production | This route rebuilds the shipped generaltranslation.com page for page in Prototemplate, with the dashboard's sign-in, onboarding, consent, device and CLI pages and their draggable state console (?state=<id> opens a state). Review galleries that Kevin asks for live here, outside gt-cloud PRs. | src/app/d/production/, src/components/plate/; gt-ship section 3 |
| /dev/states | The dashboard's /dev/states route is a development-only gallery of every sign-in and onboarding state along the real path, paged by a console in the corner and signed in through a seeded local session. Release PR #5063 removed it from main, and it lives on the branch k/dashboard-dev-gallery (2026-10-01), which is checked out in its own worktree and never carried into a PR. | gt-cloud branch k/dashboard-dev-gallery; gt-cloud's own gt-dashboard skill |
| The review server | The review server is the running dev server Kevin reviews a branch on. The landing's pnpm dev serves localhost:3001 and Prototemplate serves localhost:3005, each extra worktree takes a port of its own, and the session keeps the server up and says which branch and commit it serves. | gt-local-dev section 1; gt-website section 2; prototemplate section 7 |
| motion/ | The motion/ folder in Kevin's Prototemplate checkout is untracked, and the Videos session builds the films there with HyperFrames from the brief motion/MOTION.md. Other sessions read it and never stage, edit or delete it, and the published copies live in public/media/ and on /motion. | gt-films section 1 |
| The scratchpad | The scratchpad is the temporary folder Claude Code gives each session for specs, context files, harnesses and recorded fixtures. Nothing in it reaches a PR, and its files can vanish at the desktop app's date change, so durable work goes into a repository. | gt-ship section 3; gt-local-dev section 6 |
| Term | Meaning | Where it lives |
|---|---|---|
| Rail | A rail is the single hairline on each side of the page column, drawn once by the column wrapper (.tc-rail with border-inline: 1px solid var(--tc-hair)). Since 2026-09-28 a column has one rail on each side, and the single-rail lint holds it. | DESIGN.md section 3; gt-landing-pages "Rail and bands" |
| Band | A band is a horizontal section of a landing page. A band inside the column (.tc-sec) draws only its bottom rule, and a full-bleed band (.tc-band.tcb, .v0-dep) spans the viewport and draws its own rail pair once, at the column edges. | gt-landing-pages "Rail and bands" |
| Hatch spacer | A hatch spacer is a strip 28 to 40px tall with a hairline on each edge over a 45 degree hatch. It separates home bands (.v0-hatch) and rows inside a section (.tc-hatch), and it owns both of its edges, so the section before it drops its bottom rule. | gt-landing-pages "Hatch spacers"; DESIGN.md section 2 "Sanctioned devices" gives the shell's version, the book page's band (.pt-book-band), which also draws its own rule on both edges (2026-10-06) |
| Seam | A seam is the 1px line where two rows, cells or sections meet, formed by the row's hairline ground showing through a 1px gap. DESIGN.md section 10 also calls the slide-to-reveal handle the seam (RevealSeam, --seam-cut). | DESIGN.md sections 2 and 10 |
| The row owns every seam | This ownership rule for grids says that the row draws every structural line between its cells, cells draw no borders, and a framed cell shows the row's ground through a 1px padding reveal. Where a row meets a line that already exists, the cell sits flush and drops that side's reveal. | DESIGN.md section 2 "Ownership"; gt-landing-pages "The row owns every seam" |
| BentoRow and BentoCell | These two grid primitives implement that rule. The row is a grid with gap-px over the --tc-hair ground (props cols, eqHeads and headH), and the cell has no border props (framed adds the reveal). | gt-cloud:apps/landing/src/components/landing/shell/Bento.tsx, src/components/shell/Bento.tsx; gt-components |
| Registration cross | A registration cross is a small 1px plus seated exactly where two real hairlines cross. Kevin calls them corner crosses and DESIGN.md calls them border crosses, each landing section seam paints one at both rail corners, and a cross floating in app UI is ornament (2026-09-25). | DESIGN.md section 2 "Sanctioned devices"; gt-landing-pages "Registration crosses"; gt-aesthetic section 3 |
| The doubled line | The doubled line is GT's connector. One SVG path is stroked twice, a full ink stroke under a narrower stroke in the surface color, which leaves two parallel hairlines at a constant gap. It is the one sanctioned double line, also called the thread, and the GT mark draws every stroke in this grammar. | DESIGN.md section 5; src/components/shared/diagrams/DoubledLine.tsx; gt-diagrams |
| The plate | The plate is the dashboard's auth frame for the sign-in, consent, device, CLI and onboarding pages, with the form column on the left up to --plate-edge and the dithered field on the right. Elsewhere a plate is the one second surface under diagrams (--pt-plate), a layer of an isometric stack, or one of the speed marks. | gt-cloud:apps/dashboard/src/components/frame/PlateFrame.tsx, src/components/brand/FieldStack.tsx and src/app/brand-tokens.css there; the port in src/components/plate/ |
| Hairline roles | The three line weights each have one job. The hair weight (ink at 18% in light) draws large surfaces and the lines that divide a page, hair-soft (ink at 9%) draws list rows, and edge (ink at 62%) frames a picture or a capture, so a large surface drawn in the edge weight reads as a boxed image. | DESIGN.md section 2 "Line law for chrome"; src/components/viewer/tokens.css |
| The book page | Every Prototemplate page with a book head renders one structure: the page's plain name as the title with nothing drawn above it, a lead of one to three lines, the panel (Updated, then the page's facts), one rule, an optional note and contents, one hatch band, then sections opened by a Section n divider. A page differs from another only in its words and its facts. | DESIGN.md section 4, The book page; BookHead in src/components/viewer/BookView.tsx; pnpm lint:heads |
| Rounded controls, square shells | Prototemplate's corner law: the shells that hold the interface (the frame, the sidebar, the toolbar bar, the sheet, rules and bands) are square, and everything a reader presses, types into or picks up is rounded: controls at 6px, a part inside one at 5px, chips at 4px, cards and popovers at 6px. | DESIGN.md section 2, Corners; the radius tokens in src/components/viewer/tokens.css; pnpm lint:radius |
| Ink | Ink is #070707, the darkest of the four absolute colors, used for content text on paper and as the ground of the dark theme. Raised ink (#101010) is the one dark artifact surface for code, config and diffs. | DESIGN.md section 1; src/app/globals.css |
| Ink-2 | Ink-2 is the second text step, for secondary text and leads (--pt-ink-2, #3a3d44 in light). On a light surface, text a reader needs is set in ink-2 wherever titanium would fail contrast. | gt-aesthetic section 3; src/components/viewer/tokens.css |
| Titanium | Titanium is #8a8f98, the third and last text step, for captions, counters and keys. It measures 3.25:1 on white, so the dashboard plate darkens it to #6e737c for its 13px captions. | DESIGN.md section 1; gt-aesthetic section 3 |
| Paper | Paper is #ffffff, the light ground. Dark mode is a token remap in which paper collapses onto ink, so each theme has one surface. | DESIGN.md section 1 |
| GT blue | GT blue is the one accent, #2f5ce0, lifted to #86a8ff on dark bands. It marks actions, hover, highlights and icons while text and underlines stay ink, and a drawing gives it to one element. | DESIGN.md section 1; BRAND.md section 5; gt-aesthetic section 3 |
| The mobile type ladder | The mobile type ladder is the set of --tcm-* tokens that set type and spacing under 720px, declared in the engine's last 720px block and read as var(--tcm-X, <px fallback>). The mobile cut in v0-pages.css outranks the engine on the sgdh-root pages, so a change to the ladder is made in both places. | DESIGN.md section 12 |
| The svh and dvh law | On a mobile stage, whatever must reach the true screen bottom sizes from 100dvh, and every height that takes part in layout uses 100svh. A layout height in dvh grows the document each time the URL bar shows or hides. | DESIGN.md section 13 |
| Read lines | The read lines are the two scroll anchors of a scroll story. A beat locks in when its copy block's center crosses the read line at 55% of the viewport, and its copy lights as it rises through the highlight line at 80%. | DESIGN.md section 14; src/app/d/_v0/sections/FullStack.tsx |
| The moving type law | The moving type law governs a word that morphs between languages. The word is one shaped text node with lang and dir, its width is measured from a hidden probe and is the only layout property that animates, and the host drives one clock per page. | DESIGN.md section 8; src/components/shared/EverySentence.tsx; gt-motion section 5 |
| Box-air | Box-air is the mobile standard for boxed cells: block padding of 36 and 38px (--tcm-box-pt, --tcm-box-pb), 28px (--tcm-box-gap) between a box's head and its artifact, and no copy within 20px of a hairline. | DESIGN.md section 12 "The box-air standard" |
| Term | Meaning | Where it lives |
|---|---|---|
| Bayer dither | Bayer dither is ordered dither through a 4x4 or 8x8 Bayer threshold matrix, and it is the brand's only texture. A tone ramp prints as whole square cells in one ink, and alpha veils, smooth gradients and error diffusion are refused. | DESIGN.md section 7; src/lib/dither.ts; gt-dither section 1 |
| The studio field | The studio field is the GPU engine for the animated Bayer materials: createStudioField with ten BAYER_PRESETS sharing one WebGL context. Preset 02, bayer-8x8 (bayer8), is the default, the landing hero's field and the reference for material on product surfaces. | src/lib/studio-field.ts, gt-cloud:apps/landing/src/lib/studio-field.ts; gt-dither sections 2 and 3 |
| Glyph field | The glyph field is the canvas engine (createGlyphField) that rains glyphs from eight writing systems and condenses them into words, conserving matter between morphs. Its glyphs stay solid anti-aliased type, because dither on moving glyphs flickers (2026-08-06, 2026-09-28). | src/lib/glyph-field.ts, gt-cloud:packages/ui/src/lib/glyph-field.ts; BRAND.md section 7; gt-dither section 3 |
| Glyph rain | Glyph Rain is a direction, and the landing component of the same name mounts the glyph field rising (GlyphRain.tsx, drift: 'rise'). Kevin uses the name for the effect wherever it appears, such as the careers page and the pricing close. | src/app/d/glyph-rain/, gt-cloud:apps/landing/src/components/landing/shared/GlyphRain.tsx; gt-landing-pages |
| Ink field | The ink field is the closing band's material (createInkField). The glyph field's eight-script glyphs rise off the ink in the band's margins, stay clear of the content box measured from the live DOM, and answer the pointer. | src/app/d/glyph-rain/sections/band/inkField.ts, gt-cloud:apps/landing/src/components/landing/shared/InkField.tsx; gt-components references/instruments.md |
| Event horizon | Event Horizon is the direction where component walls dive into a black-hole gate, and the name also covers its WebGL lensing engine. The landing's careers and 404 heroes stand on that field, and as page-scale material in the app it was judged too heavy and deleted (2026-09-25). | src/app/d/event-horizon/, src/lib/horizon-field.ts, gt-cloud:apps/landing/src/lib/horizon-field.ts |
| The terminus board | Terminus Board is the direction with a split-flap headline over a flip-wave grid of languages, styled on an airport departures board. The landing's blog header is a split-flap board built from it, and its face settle is the one overshoot easing the landing allows. | src/app/d/terminus-board/; gt-cloud:apps/landing/src/components/blog/BlogHeroBoard.tsx and flap.ts; gt-motion section 2 |
| Gem smoke | Gem smoke is Paper Shaders' Gem Smoke material (Apache-2.0) as Glyphfield renders it, in blue, fire and ink palettes. It is the films' palette, it fills two deck section openers and the designed blog covers, and it can wrap a mark such as the bar monogram as a glass shape. | motion/kit/gemsmoke.js; gt-films sections 5 and 6; gt-deck references/full-picture-slides.md |
| Glyphfield | Glyphfield is Kevin's open-source shader and animation studio at glyphfield.com/studio, where new GT material starts before it ships as engine code or an export. Agents drive it through window.glyphfield.studio, and its name refers to the studio while "glyph field" names the engine above. | github.com/Kevin-Liu-01/Glyphfield; gt-dither section 4; gt-cloud .agents/skills/glyphfield |
| Artifact pictures | Artifact pictures are photographs and scans of objects, artworks and places, screened through the house 8x8 dither at 1 CSS px cells. They appear on the dashboard's sign-in and onboarding field, the plate port, the deck's mood slides and the transition demo on /docs, and each one shows writing, language or the earth, carries no readable English prose and credits its source. | docs/ARTIFACT-PICTURES.md; gt-dither section 5 |
| The Blue Marble standard | The Blue Marble standard is the tone and screen standard every artifact picture is cut to, taken from the Blue Marble picture of the earth on the onboarding field. A scene lands on mean tone 0.361 and standard deviation 0.342, the cutter is pnpm mood-tone, and the picture lint pins the standard file by sha256. | scripts/media/mood-tone/standard.json; docs/ARTIFACT-PICTURES.md, "The rules" items 3 and 4 |
| Speed marks | The speed marks are the seven race-type GT marks Kevin chose on 2026-09-29 to show beside the current doubled-line mark: the bar monogram, its lockup, the plate, double cut, the livery stack, the dithered monogram and the ASCII monogram. They are generated from geometry and font outlines and never redrawn by hand. | scripts/build/speed-marks.mjs (pnpm build:marks), public/marks/, /marks, deck slides 17 to 23; gt-brand references/marks.md |
| The bar monogram | The bar monogram is the speed mark that builds G and T from rectangles on a 120-unit cap, slanted forward 12 degrees, with one cut at mid cap height and three speed bars leading into the G. It holds at 16px and has lockup, dithered and ASCII versions. | public/marks/bar-monogram.svg; gt-brand references/marks.md |
| Term | Meaning | Where it lives |
|---|---|---|
| The line auditor | The line auditor (scripts/lint/lines.mjs) reads computed CSS in Chrome and fails on doubled lines, two owners on one seam, self-stacks, invisible seams and chrome borders outside the three hairline roles. The shell audit pnpm lint:lines:shell walks the shell routes against the dev server on 3005, pnpm lint:lines <url> audits any page, and SVG strokes still need 2x crops checked by eye. | DESIGN.md section 2; gt-lints section 2, references/line-auditor.md |
| The page check | The page check (pnpm check:pages) loads every page on a table of phones, tablets and desktops in both themes, reading phones and tablets as touch devices. It reports overflow, clipping, console errors, tap targets under 40px, layout shift and failed interactions in .pagecheck/REPORT.md, and it runs on the touched pages each round and on the whole site before a release. | scripts/check/pagecheck/ and its README; gt-lints section 2 |
| The practices ratchet | The practices ratchet (pnpm lint:practices) counts nine code checks per file against a baseline, among them bare useEffect, any, !important, raw hex in TypeScript and the retired rail vocabulary. It fails when a file's count rises, so recorded debt is paid down in its own changes. | scripts/lint/practices.mjs, scripts/lint/practices.baseline.json; gt-lints section 2 |
| The gt-ui oxlint plugin | The gt-ui plugin holds gt-cloud's design and copy laws as oxlint rules, among them single-rail, inter-only, icon-tiers, no-em-dash, no-eyebrow, no-smooth-scroll, mono-is-not-voice and shared-cta. Prototemplate runs a whole-file copy through pnpm lint:code and recopies it whenever gt-cloud's version changes. | gt-cloud:tooling/oxlint-plugins/gt-ui.ts, scripts/lint/oxlint-plugins/gt-ui.ts; gt-lints references/gt-ui-rules.md |
| The picture lint | The picture lint (pnpm lint:pictures) holds every artifact picture to the Blue Marble standard by hash, size, tone and credit. It runs before next build, so a picture defect stops the build, and pnpm test:pictures breaks one thing per test to prove the lint catches it. | scripts/lint/pictures.mjs; gt-lints section 2; gt-dither section 8 |
| The type lint | The type lint (scripts/lint/type.mjs, started 2026-10-05) holds Prototemplate's type to the rsms InterVariable through the type tokens. It allows no other family, no weight above 500 and no positive tracking, and it keeps mono to code and to named entries such as numbers, paths and hex values. | gt-lints "The type lint"; gt-brand references/type.md |
lint:all | Prototemplate's pnpm lint:all chains the static lints, the browser audits against the dev server on 3005 and the lint tests with &&. Run the browser gates one at a time, and read package.json for the exact chain. | package.json; prototemplate section 9 |
| The parity gate and ledgers | The parity gate is the release gate for any onboarding or auth redesign: the release keeps every tracking call and backend side effect that production had. The proof records production's ledger of side effects per scenario at the module boundaries, replays the scenarios on the release and diffs the two ledgers after every fix (2026-10-02). | gt-ship section 7 |
| pr-assets and screenshots/pr-N | The pr-assets branch is gt-cloud's orphan branch for PR screenshots, under screenshots/pr-<n>/ and linked with blob/pr-assets/...?raw=true. Older PRs used one orphan branch per PR named screenshots/pr-<n>, and the instant Vercel failure on those branches ("Root Directory apps/landing does not exist") needs no action. | gt-ship section 4, references/pr-body.md; gt-website section 8 |
| Greptile 5/5 | Greptile writes a confidence score into the PR body, and the target is 5/5 on the head commit with every Greptile thread answered and resolved; a PR does not merge at 4/5 (2026-09-13). It does not rerun on every push, so comment @greptile review again and wait about five minutes. | gt-ship section 5, references/review-loop.md, scripts/pr-bots.mjs |
| Bugbot | Bugbot is Cursor's review bot. It reruns on every push, bugbot run re-triggers it, and each finding is answered with the fixing commit or a reason before its thread is resolved. | gt-ship section 5 |
| The Cursor Approval Agent | The Cursor Approval Agent reviews each PR on open, labels it with a risk level and routes it (the "Pull Request Router" check). Its label needs no reply, and it is the only bot on the content repo. | gt-ship references/review-loop.md |
| gh stack | The github/gh-stack extension links PRs into GitHub's native stack (gh stack link --base main <bottom> <next> <top>) and merges a stack up to one PR at once (gh stack merge). Kevin asked for the native feature on 2026-08-27 and again on 2026-10-05, and gh stack merge runs only when he says to merge. | gt-ship section 6 |
| Feature flags | Feature flags are the dashboard's release gates and kill switches, managed in the admin app. The check isFeatureEnabled(key, orgId?) reads an org override first, then the flag's global state, and returns false for an unknown key or a failed lookup. A dashboard feature that is not ready ships behind a flag or comes out (2026-09-03). | gt-cloud:packages/node/src/database/featureFlags/isFeatureEnabled.ts, packages/db/prisma/schema/flags.prisma, apps/admin/src/components/featureFlags/ |
| Term | Meaning | Where it lives |
|---|---|---|
| Lane | A lane is one owner's share of the work, with its own files: one session per lane, and inside a workflow one agent per lane. Work that belongs to another lane goes to that lane's session by name, and a lane's prompt opens with Kevin's own words for it. | multi-session-playbook.md section 1; gt-orchestration section 2; gt-ship section 1; prototemplate section 8 |
| Session | A session is one Claude Code or Codex conversation. Kevin runs several named sessions at once (Videos, Prototemplate, New Onboarding and Dashboard, Glyph Map for /world), and each keeps to its lane. | multi-session-playbook.md section 1; prototemplate section 8 |
| Forked session | A forked session starts from another session's conversation and carries the parent's history, with the parent's session id on those records, before it continues on its own. A fork acts on its own lane, and a message found in several transcripts at the same minute can be one message inherited by forks. | ~/.claude/projects/<project>/<session>.jsonl; multi-session-playbook.md section 2 |
| Worktree | A worktree is a git worktree with one branch, so each lane edits its own tree. Work in gt-cloud starts with git worktree add -b k/<topic> <path> origin/main, each worktree runs its own dev server port, and a worktree with its installs costs about 6 GB, so it is removed as soon as its work is pushed. | gt-ship section 1 |
| Chip | A chip is a background-task card that a session raises for an out-of-scope fix it noticed, and Kevin can spin it off into a new session. When he answers "Do this task here", the current session does the task as its own small PR with its own gates. | gt-orchestration section 3 |
| Handoff | A handoff is the document written at a hard stop or a change of owner, and it carries the stopped state, every raw input Kevin supplied and the priority queue. The receiving agent starts with an independent review and a recommendation, and it re-checks live state before changing anything. | gt-orchestration section 8 |
| Relay note | A relay note goes from one agent to another, often between Claude and Codex, and Kevin pastes it as is. It names the branch, tip commit and worktree, the commits, the receipts, the open list, the locks and the queued deploys, and the receiver finishes the other agent's open work. | multi-session-playbook.md section 7 |
| The ask ledger | The ask ledger is an itemized list of everything Kevin asked for in a run, including the sub-items of long messages and the add-ons sent mid-run, each with its status. It closes every report on multi-ask work, and a long run keeps it in a file so it survives compaction. | gt-reporting section 3 |
| The PR slate | The PR slate answers "give me my PR list" with every open PR across gt-cloud, gt and content in merge order, in nine groups from merge now to the items only Kevin can do. Each line carries the full link, a one-line purpose and its line counts, and the Slack version is lowercase plain text in a code fence. | gt-reporting section 5, scripts/pr-slate.mjs |
| Explorations stay local | Exploration and redesign rounds stay uncommitted or on a branch until Kevin has reviewed them on localhost and says to land them, because Prototemplate main deploys the public site (2026-09-14). | gt-aesthetic section 5; gt-ship section 8; gt-explorations |
| Kevin merges | An agent merges a PR or runs gh stack merge only when Kevin says so in that session. | gt-ship sections 5 and 6 |
| The agent guide | AGENTS.md at the root of Prototemplate is the entry point for an agent: the read order, the principles in brief, which skill to load for a task, the house rules, and how to import the hub into another project. CLAUDE.md imports it for Claude Code. | AGENTS.md; /handbook/agents |
| Term | Meaning | Where it lives |
|---|---|---|
| "image 1" | "Image 1" names the first image attached to the message, and "image 2" the second, in attach order. Each numbered note maps to a region of its image, and the image is the spec. | gt-reporting section 7; gt-aesthetic section 5 "Rules of the loop" |
| "equivalent" or "equal" | Both measures take the larger value (2026-09-30). | gt-aesthetic "Reading Kevin's asks", references/verdicts.md |
| "keep its aesthetic" | The geometry and the material stay, and only the named thing changes. | gt-aesthetic "Reading Kevin's asks" |
| "smaller" or "larger" | The value moves to the next rung of the existing ladder, as when the 13px card facts went to the 12px rung (2026-09-28). | gt-aesthetic "Reading Kevin's asks" |
| "fully" | The work covers every section, capability and detail with real diagrams, visuals and code. A placeholder or a partial pass does not count. | operating-principles.md section 15 |
| "continue all" or "keep going" | Resume every open lane and every unfinished item, the hard ones included, without pausing between waves. In a forked session the words cover the fork's own lane, and after a restart or a compaction the work resumes from the plans kept on disk. | operating-principles.md section 1; gt-orchestration section 6; multi-session-playbook.md section 2 |
| "no new spawns" | Agents already running finish, their work is committed and pushed, and no new agent starts (2026-08-06). | gt-orchestration section 1 |
| "show me" | Put the result where Kevin can see or hear it: before and after crops, a running localhost URL, or a small HTML gallery or listening page for a set. | gt-reporting section 1; operating-principles.md section 8 |
| "second pass" | A second pass reviews a finished page against its reference before it is shown, and pages that skipped one are redone (2026-08-17). | gt-aesthetic section 2 |
| "try again" | After an API error or a spend limit, re-run the step that failed, starting from a fresh read of the state. | multi-session-playbook.md section 3 |
| "commit and push" | Commit and push at once to every target he names, staging explicit paths. | gt-ship sections 2 and 8 |
These terms still appear in older messages, commits and skills. The ruling that retired each one, its date and its reason are recorded in decisions.md.
| Term | What it was | What replaced it |
|---|---|---|
| Switzer | Switzer was a second typeface used beside Inter in the July rounds. | Retired 2026-09-18: Inter is the only face, gt-cloud #4887 removed the files, and inter-only holds it (decisions.md). |
| Lenis | Lenis was the smooth-scroll library behind the July scroll stories. | Retired from the site 2026-07-31: scrolling is native and no-smooth-scroll holds it. The presenter's last Lenis code was removed on 2026-10-08. Its paging still animates the jumps between slides, under a named exemption in .oxlintrc.json (decisions.md). |
| Double rails | The double rails were a second pair of lines 10px outside each rail, also called the outer pair or doubled rails. | Retired 2026-09-28 in gt-cloud #5007: one rail per side, held by single-rail and the practices ratchet's rail checks (decisions.md). |
| DirectionDock | DirectionDock was the control pill on the direction pages in July. | Retired 2026-07-31 and deleted 2026-09-08. DirectionCorner (src/components/viewer/DirectionCorner.tsx) took its place and hides under ?chrome=0 (decisions.md). |
| apps/redesign on port 3006 | The July explorations app ran from apps/redesign in a gt-cloud worktree on the branch redesign/diagram-standard. | Retired as the primary copy on 2026-09-08: Prototemplate main holds the canon and serves on 3005, and prose that names 3006 is stale (decisions.md). |
| The dictionary pictures | These artifact pictures showed pages of readable English: the dictionary, Johnson, OED and Oxford pictures. | Retired 2026-10-05: artifact pictures carry no readable English prose, and the picture lint rejects the retired names (decisions.md). |
package.json; .oxlintrc.json; src/lib/directions.ts, src/lib/surfaces.ts and src/components/viewer/tokens.css; scripts/lint/practices.mjs; motion/kit/gemsmoke.js (header); all read 2026-10-05.prototemplate, gt-aesthetic, gt-ship, gt-lints, gt-dither, gt-landing-pages, gt-website, gt-components, gt-brand, gt-deck, gt-films, gt-motion, gt-diagrams, gt-orchestration, gt-reporting, gt-local-dev and gt-explorations. The handbook pages gt-product-map.md, operating-principles.md, multi-session-playbook.md and decisions.md hold the procedures behind the working terms and the rulings behind the retired ones.apps/landing/package.json (dev), apps/dashboard (components/frame/PlateFrame.tsx, components/brand/, the context routes), packages/node/src/database/featureFlags/, packages/db/prisma/schema/flags.prisma, apps/admin/src/components/featureFlags/, apps/landing/src/lib/ (studio-field.ts, horizon-field.ts, report-card/) and apps/landing/src/components/blog/; the branch k/dashboard-dev-gallery at ebb900124.packages/*/package.json for the package names.In his Claude Code and Codex sessions, Kevin Liu rules on how General Translation (GT) work is done, across process, product, design and the repositories. This log lists the rulings that set or replaced a standing practice from 2026-07-31 to 2026-10-10. Each row gives the date, the ruling, the practice it replaced, his words where the ruling needs a source, and the lint, gate or skill that holds it. Read it before you change a GT standard, and whenever an older note, transcript or skill gives conflicting guidance.
gt-aesthetic, with Kevin's words and what was wrong. Their rows here are one line and link there.content: for generaltranslation/content, which holds the docs and blog source.| Date | Ruling | Replaced | Kevin's words | Held by |
|---|---|---|---|---|
| 2026-07-31 | Scrolling is native on every GT surface: no smooth scrolling, scroll hijacking or inertia library. A scroll story scrubs its timeline on the page's own scroll. | Lenis smooth scrolling, which Kevin asked for on 2026-07-22, 07-28 and 07-30 for the scroll stories and the presenter | "remove smooth scrolling from everything, i hate how it keeps going after i stop scrolling" | no-smooth-scroll in gt-cloud (landing, packages/ui) and Prototemplate pnpm lint:code, with /present exempt in Prototemplate's .oxlintrc.json; gt-motion sections 1 and 6 |
| 2026-08-01 | Monospace is for numbers and code only. Headings, paragraphs, labels, pills and social cards are set in Inter. | Mono labels, pills and OG text in the July explorations | "mono should only be used for numbers." | mono-is-not-voice; inter-only allows Geist Mono for code only; gt-brand section 4 |
| 2026-08-04 | Mock layouts and plan images are literal specs, and a display head holds two lines at most. Locadex appears as an isometric drawing that carries its mark, and a gif of it is refused. | Sections that kept the copy and drifted from the Figma layouts; three-line heads; a Figma note proposing a gif of Locadex | verdicts.md, 2026-08-04 | no-gif-mark; Prototemplate's skills/gt-aesthetic/scripts/measure-type.mjs flags a display head over two lines |
| 2026-08-06 | The Dossier (/d/singularity-dossier) is the completed reference for the site. Product surfaces follow the brand deck since 2026-09-25, for the brand the 2026-10-06 row below holds, and for the site the 2026-10-07 row below holds. | The July bar of generaltranslation.com and resend.com | verdicts.md, 2026-08-06 | gt-aesthetic section 1; BRAND.md section 8 |
| 2026-08-07 | Every crawler and agent may read the public site, and no rule blocks an AI crawler or a public page. Only the staging build disallows crawling. | None; a new standing rule | "make it so that all robots and agents can crawl us" | gt-cloud's apps/landing/src/app/robots.ts; gt-website section 7 and its review checklist |
| 2026-08-07 | A heading carries no trailing period. Body sentences keep theirs. The deck followed on 2026-09-08. | Landing section headings that ended in a period | "remove the periods from headers to standardize. our hero header has no periods"; on the deck: "i dont thinkn we need periods everywhere" | no-heading-period (gt-cloud #5007, Prototemplate pnpm lint:code); gt-voice, Headings and casing |
| 2026-08-08 | The staging build translates Spanish only. Production keeps the full locale roster. | Staging translating Japanese and Spanish | "only es for staging." | gt-cloud's apps/landing/staging.gt.config.json (["es"]) beside gt.config.json, run by scripts/deploy-landing.sh --staging; gt-website sections 3 and 8 |
| 2026-08-11 | For the gt-cloud redesign stack (landing, sign-in and dashboard), the production landing page is canonical. That page is gt-cloud #4213, built from the Dossier. Prototemplate and the toolchain lend isolated mechanics, assets and example copy, and the toolchain enterprise and pricing prototypes are the spec for those two pages. Product surfaces moved to the brand deck on 2026-09-25. | Copying the old toolchain page shell into new pages | "The production GT landing page is canonical." (a handoff brief he sent to the redesign sessions) | gt-aesthetic section 1; gt-landing-pages, The reference |
| 2026-08-11 | GT surfaces avoid mono as the brand voice, smooth scrolling, robot or sparkle AI icons, flag soup, AI gradients and glass, eyebrows, and em dashes in rendered text. | The July explorations' eyebrows, gradients, glass and mono labels | "3 lines of text stacked to say the same thing" (on eyebrows). verdicts.md, 2026-08-11 | no-eyebrow, no-em-dash, mono-is-not-voice, no-smooth-scroll, no-raw-locale-flags, icon-tiers; AI icons, gradients and glass by review (gt-brand section 7) |
| 2026-08-11 | No invented content. Copy, quotes, metrics, prices and product vignettes come from production, the benchmarks or GT's published pages. | Placeholder testimonials and invented enterprise vignettes | Kevin, 2026-08-05: "like these arent real quotes". verdicts.md, 2026-08-11 (round two) | gt-aesthetic review checklist; operating-principles.md rule 9 |
| 2026-08-12 | Once Kevin picks a variant, the alternatives leave the code and stay in git history. | The variants program's switchers and dock, about 16,000 lines | "get rid of the variation system" | verdicts.md, 2026-08-12; gt-aesthetic section 2 |
| 2026-08-14 | A landing button is the shared Cta component with a Title Case label, and its tracked value is one of the existing PostHog location slugs. A new slug needs Kevin's approval. | Buttons built by hand on each landing page | Decided in the landing button round (gt-cloud #4348) | gt-ui/shared-cta and gt-ui/cta-title-case; gt-landing-pages, gt-components |
| 2026-08-14 | Flags render only through the shared LocaleFlag SVGs, on every surface including the dashboard. | Emoji flags | "NO EMOJIS!!! USE OUR FLAGF BLIB RARY!!!" | no-raw-locale-flags on gt-cloud's dashboard, landing and packages/ui; packages/ui/src/components/ui/LocaleFlag.tsx; gt-components |
| 2026-08-15 | PR titles and bodies are lowercase, plain technical English, with no em dashes and no mention of Claude. Kevin repeated it on 2026-09-08. | Generated PR prose | "write up the prs with no reference to claude code and no emdashes in lower case"; 2026-09-08: "make the pr language in my standard technical english lower case" | gt-voice, Code comments, commits and PR descriptions; gt-ship section 4 and references/pr-body.md |
| 2026-08-17 | Every page gets a second pass against its reference before it is shown. | Pages shipped on their first pass (contact, sign-in, legal) | verdicts.md, 2026-08-17 | gt-aesthetic section 2; quality-bar.md |
| 2026-08-17 | A docs or blog URL never dead-ends. A near miss redirects to its page, a folder goes to its index, and an unmatched path lists the closest pages. | The plain 404 | "u should probably never hit a 404 on docs and blogs" | resolveDocsPath in gt-cloud's apps/landing/src/lib/slug-suggest.ts, and apps/landing/src/lib/blog-near-miss.ts (gt-cloud #4359, merged 2026-09-09); gt-website references/docs.md |
| 2026-08-26 | A docs page description opens with an action verb such as "Use" or "Install". Descriptions that open with "Learn how" or "Learn what" are rewritten. | Get Started captions that began "Learn how" | "learn is a terrible verb to start with, we like Use and install" | content:DOCS-SKILL.md (the rule for descriptions) |
| 2026-08-27 | Stacked PRs use GitHub's native stack, linked with gh stack link. Kevin reaffirmed it on 2026-10-05. | A Stacked on #N line in the body as the only link between PRs | "use the ACTUAL github stack feature"; 2026-10-05: "i meant use the actual pr stack feature" | gt-ship section 6 (github/gh-stack) |
| 2026-08-27 | gt-cloud commits carry Kevin's General Translation work identity. | Commits under his personal email, which gt-cloud's Vercel projects refuse to deploy | "gt-cloud deployments on vercel wiwll not accept my home email authored commits" | gt-cloud's repository user.email, which covers every worktree; gt-ship section 2 |
| Date | Ruling | Replaced | Kevin's words | Held by |
|---|---|---|---|---|
| 2026-09-02 | Integration docs go under Integrations. A top-level docs section is reserved for a framework, plus the CLI and the Platform, and Integrations is the default when in doubt. | rrweb as a top-level docs section | "default to putting in the integrations section when in doubt" | content:DOCS-SKILL.md, "When something gets its own top-level section"; the top-level list is pinned in content CI by content:scripts/validate-docs-structure.ts (content #501) |
| 2026-09-03 | An unfinished dashboard feature ships behind a feature flag or does not ship. | Unfinished dashboard pages released without a gate | A team decision Kevin relayed with the fix request | isFeatureEnabled(key, orgId?) in gt-cloud's packages/node/src/database/featureFlags/isFeatureEnabled.ts, with flags managed in the admin app; glossary.md |
| 2026-09-08 | Review requests go through GitHub reviewer requests, which Linear routes to the reviewer in Slack. Kevin chooses the reviewers. | Asking for reviews in a Slack channel | "keep this in mind for all our prs nnow, i connected everything in linear properly" | gt-ship section 5, Reviewers; gt-cloud's APPROVAL_POLICY.md |
| 2026-09-08 | Writing is plain technical English: no em dashes, metaphors, contrast pairs, fragment rhythm or signposts. | Agent prose with metaphors and parallel constructions | "do it in stragiht standard technical english without any weird metaphoors" | gt-voice; no-em-dash; the humanizer skill in Kevin's wiki |
| 2026-09-09 | The rebrand works from first principles. Only the company's legal name and the gt-* library names customers install are fixed. | None; the brief for the brand review | "just a few things that cant change (the legal name of the company, general translation, annd the nname of libraries our customers use" | No gate; the fixed names are defined in BRAND.md section 1 and gt-brand section 1 |
| 2026-09-10 | Pricing shows whole-dollar amounts of $1 or more without cents. Cent prices and amounts under a dollar keep two decimals. | Two decimals on every price | "turn $2.00 and $1.00 and anything like this to $2 and $1" | gt-cloud's packages/ui/src/components/pricing/dollar-format.ts and apps/landing/__tests__/usage-dollar-format.test.ts (gt-cloud #4785) |
| 2026-09-10 | Public writing is written for a reader who knows nothing about Kevin, the team or the repository. It defines the product once, opens each section with the point that reader can reuse, and keeps PR numbers and internal names out of the prose. | Drafts that narrated process and cited internal names | "be so much more straightforward and clear to a user who has no idea who I am" | gt-voice; quality-bar.md, Copy |
| 2026-09-10 | The docs sidebar on a 404 page is the stock flat tree. | A client-side pathname override that kept the section tree and flashed the flat tree before hydration | "i cann see it flash. should we just have what it is before if its what fuma nama does?" | gt-cloud #4773 (the revert); gt-website section 4 |
| 2026-09-11 | When Kevin wants a gt-cloud PR merged on one approval, its commits carry no Claude co-author trailer. | Trailered commits, which the repository rules send to a second human approval | Decided on gt-cloud #4795 | gt-ship section 2, The Claude trailer |
| 2026-09-14 | Exploration rounds stay on a branch or uncommitted until Kevin reviews them on localhost and says to land them. The directions in a round differ in silhouette, material and action. | A ten-direction round pushed to Prototemplate main before he saw it | verdicts.md, 2026-09-14 | prototemplate section 9; gt-ship section 8; gt-explorations |
| 2026-09-15 | Git pushes trigger no landing preview deploys. Main and staging deploy, and a preview is deployed by hand from the CLI. | A preview deploy on every push to a PR branch | "this is running up a lot of costs" | gt-cloud's apps/landing/vercel.json (git.deploymentEnabled), gt-cloud #4828; gt-website section 8 |
| 2026-09-15 | PostHog loading and analytics initialization stay as they are in any PR that is not about analytics. | A PR that deferred PostHog initialization in packages/ui/src/components/analytics/PostHogProvider.tsx | "dont touch posthog. evaluate your chnages for stuff that is high risk like posthog" | gt-website section 4; for onboarding, the parity gate in gt-ship section 7 |
| 2026-09-18 | Inter is the only typeface. gt-brand section 4 names the few exceptions, such as the Prototemplate nameplate and the presenter. | Switzer as the display face beside Inter (the deck dropped it on 2026-09-08) | verdicts.md, 2026-09-18 | inter-only; gt-cloud #4887 deleted the Switzer files; gt-brand section 4 |
| 2026-09-24 | Icons come in two tiers: Heroicons solid for marks that carry meaning, Lucide outline for controls. Brand marks stay as drawn. | Lucide outline glyphs everywhere | Kevin, 2026-09-28: "look for ANYWHERE ELSE icons nneed to be synced" | icon-tiers (the rule in gt-cloud #4909, the lint in #5007); gt-components references/icons.md |
| 2026-09-24 | The identity stays in the neutral, scalable territory: a grid, one type system, one accent blue, and parts that hold at 16px and in a terminal. | A more opinionated creative territory proposed in the brand review | "need to be scalable in future. not incredibly opinionated. feel like switzerland" | BRAND.md; gt-brand sections 2 and 3; no gate |
| 2026-09-25 | Prototemplate reserves all rights to General Translation, Inc. The repository is public to read, and third-party fonts, icons and adapted skills keep their own licenses. | The MIT license | "make prototemplate not mit licensed anymore." | LICENSE; README, License |
| 2026-09-25 | A coding agent runs the Setup for Agents prompt end to end on a fresh app before it ships, and again after any change to the prompt, the CLI or the quickstarts. | Shipping a generated prompt untested | "TRY IT OUT SEE IF IT WORKS AND YOU CAN GET TO CREATING A TRANSLATION". He asked again on 2026-09-30 and 2026-10-01. | gt-website section 6 and references/pages.md |
| 2026-09-25 | PR screenshots go into the body from the first push and are refreshed after every visible change. | Screenshots added at the end of review | "add the screenshots of this to the pr the whole time" | gt-ship section 4 |
| 2026-09-28 | Each column has one rail on each side, drawn once by the .tc-rail wrapper. | Doubled outer rails 10px outside each column | "make sure theres nno rules that leead to two side rails" | single-rail (gt-cloud #5007, Prototemplate pnpm lint:code); retired-rail-vocabulary in Prototemplate's scripts/lint/practices.mjs; DESIGN.md section 3 |
| 2026-09-28 | The theme control draws the circle glyphs ◐ and ◑. | Sun and moon icons | verdicts.md, 2026-09-28 | The shared ThemeToggle in gt-cloud's packages/ui/src/components/frame/ThemeToggle.tsx; no-theme-icons on open gt-cloud #4977, which has not reached main |
| 2026-09-28 | PR screenshots are tight before and after crops of the changed elements, with the same clip on main and on the branch. | Full-page screenshots in two-column tables | "make this pr page much much easier to read and show the images" | gt-ship section 4 and references/pr-body.md |
| 2026-09-29 | No offer, credit or capability appears in the product before the backend delivers it. | A sign-up credit offer in the redesigned onboarding that billing did not yet grant | "we only show it if we actually do it" | The parity gate admits only stricter checks and recovery paths as additions (gt-ship section 7); review |
| 2026-09-30 | The hero's second button is Setup for Agents, which copies the tested agent prompt. | A Docs button | "change docs into setup for agents with a copy icon and make that into its own pr" | HeroSecondButton.tsx and CopyPromptButton.tsx in gt-cloud's apps/landing/src/components/landing/sections/agent/; the prompt's one source in content:docs/en-US/overview/for-coding-agents.mdx, served at /agent-prompt.md (gt-cloud #5049); gt-website section 6 |
| Date | Ruling | Replaced | Kevin's words | Held by |
|---|---|---|---|---|
| 2026-10-01 | GT's identity work takes dithered artifact pictures in black, white and blue as a reference. Kevin sent them to the brand agency with the race-type speed marks he chose on 2026-09-29, and he called the marks an experiment. The doubled-line monogram stays the current mark. | None; it builds on the 2026-09-24 territory | "artifacts, dithered, we like this aeshteitc of black white blue"; on the marks: "nothing but funnu experiementation" | gt-dither (the pictures); scripts/build/speed-marks.mjs and /marks in Prototemplate; gt-brand section 5 |
| 2026-10-01 | A release PR carries no development code. Galleries, stand-ins, test-only hooks, dev routes, recorded fixtures and harnesses stay on their own branch or in the scratchpad. | A release branch that carried the dev states gallery and a 24k-line parity suite | "please cleanup / remove all dev / temporary 'testing' code" | gt-ship section 3, Verification instruments stay out, and skills/gt-ship/scripts/pr-size.mjs |
| 2026-10-02 | An onboarding or auth redesign keeps every tracking call and backend side effect that production has. Parity is proven by recording production, replaying the release and diffing the two ledgers. | Judging parity from a reviewer's list of intended changes | "we need the new onboarding to keep all the tracking and backend stuff that the old onboarding did" | gt-ship section 7, The parity gate |
| 2026-10-02 | A PR's line count is part of its review. Before a push the diff is grouped into product, tests, fixtures, scripts and lockfile, and every large group has a reason. | A release PR of about 39,500 lines, most of them a recorded parity suite | "why is 5063 39.5k lines? fix this." | skills/gt-ship/scripts/pr-size.mjs; gt-ship section 3 |
| 2026-10-02 | A public repository with a third-party licensing problem stays public, and the third-party material is removed or replaced. Kevin ruled this in one of his own repositories, and it applies to Prototemplate. | Making a repository private to settle third-party terms | "never privatize the repo. simply remove third party models." | Review; each picture's source and license are confirmed when it is added (gt-dither sections 5 and 7); LICENSE lists Prototemplate's third-party material |
| 2026-10-02 | A PR is closed only when its change is already on main. The closing comment names the merged PR that carries the change, and the branch stays. Every other PR stays open for Kevin to decide. | Closing stale and superseded PRs in bulk | "only delete prs that are already in the codebase" | gt-reporting section 5 (close candidates in the PR slate); the closing comment is the check |
| 2026-10-03 | Each session owns one lane, and work for another lane goes to that session by name. | Sessions doing each other's work, such as a site session making films | "that videos one should be making them, and this one is just for prototemplate work" | gt-ship section 1; prototemplate section 8; multi-session-playbook.md |
| 2026-10-03 | /world is organized by language. It names and outlines no country or territory, and its facts come from CLDR. | The first brief, a map of countries drawn in their languages | "avoid political sensitivity without making it look forced" | gt-website section 6 (branch k/language-map, unmerged on 2026-10-05) |
| 2026-10-05 | Prototemplate is the hub and wiki of how GT work is done, with a curated skill set in skills/<slug>/ that holds only the skills in real use. | A generated dump of 261 wiki skills on /skills | "this prototemplate is tupposed to be my hub for general trnalsation work"; on /skills: "we only need to show and store the ones weve actually been using" | prototemplate section 10; /skills renders only the set |
| 2026-10-05 | Every element resolves through the type tokens to one face, the self-hosted rsms InterVariable 4.1. | Faces set differently from element to element, and Google's build of Inter | "esepcially encorce the CORRECT RASMUS INTER". verdicts.md, 2026-10-05 | inter-only in gt-cloud; pnpm lint:type in Prototemplate (scripts/lint/type.mjs, from the 2026-10-05 type round); gt-brand section 4 |
| 2026-10-05 | Artifact pictures carry no readable English prose. | The dictionary, Johnson, OED and Oxford pictures | verdicts.md, 2026-10-05 | pnpm lint:pictures and docs/ARTIFACT-PICTURES.md in Prototemplate; check:artifact-pictures on open gt-cloud #5133; gt-dither |
| 2026-10-05 | A book head's title is the page's plain name (Brand, Documentation, Handbook), and the head's right side is a panel: the last update, then the page's own facts, each with an icon. | Titles that were phrases ("The brand", "Prototemplate docs") beside a plain meta table | "dont make it "the brand" "prototemplate docs" it should be Brand and Documentation and make the right side of those headers better, show last update, use icons" | src/lib/page-names.ts; pnpm lint:heads (S1, S5, H5) and pnpm lint:updated; prototemplate section 3 |
| 2026-10-05 | Rounded controls, square shells: buttons, fields and segmented controls round at 6px, chips and key caps at 4px, cards and popovers at 6px, and only the UI shells are square. | Square boxes everywhere outside a short list of exceptions | "i want to enforce and keep this amount of border rounding instead of forcing Boxes. boxes only for ui shells" | pnpm lint:radius (scripts/lint/radius.mjs); DESIGN.md section 2, Corners; gt-aesthetic section 3 |
| 2026-10-06 | Every page with a book head renders one structure: nothing is drawn between the sheet's top border and the title, one hatch band carries a rule on both edges, and the mast, the panel and the section dividers are the same on every page. | Heads that differed from page to page, a guide line through the title, and a band without its top rule | "see how the brand is touching the up line and the spacer is missing a border above it? standardize our presentation more" | pnpm lint:heads and pnpm lint:heads:live; DESIGN.md section 4, The book page; verdicts.md, 2026-10-06 |
| 2026-10-06 | The brand's head shows a reading time like the other pages and names no reference application. The brand has moved past the Dossier, and BRAND.md section 8 ("The completed reference") waits for Kevin's decision. | The Dossier as the brand's completed reference (the 2026-08-06 row) | "why is the dossier the reference for Brand? its evolved so much more since then", then "just give it a reading time like the other pages." | /brand's panel; verdicts.md, 2026-10-06; no gate |
| 2026-10-06 | Simplify. The platform feature that removes code comes before new code, and machinery a review asks to test is removed when it need not exist. | A caption card that measured its own height with an observer and component state | "Beautiful but don't complicate. Always simplify." | operating-principles.md rule 12; gt-aesthetic |
| 2026-10-07 | The shipped site is the reference for landing and marketing pages: generaltranslation.com, built from apps/landing on gt-cloud origin/main. The Dossier stays in the gallery as the direction the site grew from. BRAND.md section 8 ("Where it ships") names where the identity ships: the site, the product surfaces that follow the brand deck, the films on /motion and the marks. | The Dossier as the completed reference for the site (the 2026-08-06 row), and the decision on BRAND.md section 8 that the 2026-10-06 row left open | "fix the dossier references." | gt-aesthetic section 1; verdicts.md, 2026-10-07; BRAND.md section 8; no gate |
| 2026-10-07 | A skill's title and a document's h1 name the thing with no leading "The": Brand and the correct Inter, Brand deck, Landing page grammar and GT website for the skills, and Graphics pipeline, Libraries and Ship loop for the documents. | Titles that opened with "The" ("The brand deck", "The Ship Loop") | "fix the The titles." | scripts/build/skills.mjs fails a skill title that opens with "The" and a skill whose h1 differs from its metadata.title; review holds the documents' h1s; the 2026-10-05 row on head titles |
| 2026-10-07 | Prototemplate is served at three addresses: www.prototemplate.com from the team's Vercel project, and prototemplate.vercel.app and prototemplate.kevinliu.studio from Kevin's personal project. On 2026-10-07 prototemplate.kevinliu.studio served the same deployment as prototemplate.vercel.app. | www.prototemplate.com as the one live domain, with prototemplate.vercel.app as the personal project's alias | "prototemplate.com and vercel.app are both ok, but prototemplate.kevinliu.studio shoul dbe a thing too." | README.md, What is here; gt-ship section 8 |
| 2026-10-07 | The film cuts in review are approved for the site. jihe-yuanben is published under CC BY-SA 4.0, and its credits file says so. Kevin signed off the last open items, the non-English takes that waited for a native listener, and expects those films to be updated. | Cuts held for Kevin's approval, the share-alike license of jihe-yuanben left to his decision, and non-English takes held for a native listener | "film cuts approved. jihe is yes. and the last ones are signed off, theyre going to be updated anyways" | gt-films sections 7, 8 and 11; public/motion/published.json |
| 2026-10-08 | On a page that already exists, "enhance" keeps the page and adds to it. Copy beside a team photo names nobody in it and states no headcount. | A careers rebuild that dropped the hero, the customer band and the original copy | "keep the original write up" | operating-principles.md rule 15; quality-bar.md, Copy; gt-aesthetic |
| 2026-10-10 | Prototemplate commits author as Kevin with his GitHub noreply address and keep the session's Co-Authored-By: trailer. | Commits under his personal or his work address | Answered in the review of the system v2 plan | docs/SHIP-LOOP.md section 6; gt-ship section 2 |
| 2026-10-10 | www.prototemplate.com is the canonical address. prototemplate.vercel.app and prototemplate.kevinliu.studio stay as aliases. | prototemplate.vercel.app as the address in the site metadata and llms.txt | The recommended default, accepted in the same review | SITE_URL and public/llms.txt; README.md, What is here |
| 2026-10-10 | A customer or partner is named in this repository only where generaltranslation.com shows it. | A list in BRAND.md section 9 that named a customer the site no longer showed | The recommended default, accepted in the same review | BRAND.md section 9; pnpm lint:public and its private denylist |
| 2026-10-10 | gt-cloud's internal material stays out of the public skills: its dev-environment internals and the file maps of its private packages live in gt-cloud's own skills. PR numbers and branch names may appear. The copies of the gt-ui lint plugin and the plate stay at the copy taken, with no re-sync until gt-cloud's product and infrastructure owners agree. | Skill sections that described gt-cloud's dev infrastructure and package file maps | The recommended default, accepted in the same review | pnpm lint:public; scripts/lint/copies.json; prototemplate |
| 2026-10-10 | The curated skills reach gt-cloud sessions as project links made by pnpm skills:install --project on a gt-cloud checkout and listed in that checkout's .git/info/exclude, so nothing gt-cloud tracks changes. | Skills that no gt-cloud session loaded | Answered in the same review | README.md, Skills |
| 2026-10-10 | The film sources in motion/ are tracked through an allowlist: the kit code and one entry per published film. Unreleased films stay untracked, and Kevin reads the first staged diff. | motion/ untracked as a whole | Answered in the same review | The motion block in .gitignore; gt-films |
| 2026-10-10 | Finished round specs move to docs/archive/, with machine paths replaced by placeholders. | Finished and abandoned specs kept beside the live sources | The recommended default, accepted in the same review | docs/archive/README.md; pnpm lint:public |
| 2026-10-14 | /docs is a docs site of ten pages on how Prototemplate works, built on fumadocs with the notebook layout generaltranslation.com/docs runs, outside the viewer shell, and written to serve as a template for another project. The canon documents (AGENTS.md, BRAND.md, DESIGN.md, ARCHITECTURE.md, the ship loop, the tools, the libraries with the build log, the graphics pipeline and the artifact pictures) joined the handbook at /handbook/<slug>, and their old /docs/<slug> addresses redirect there. | /docs as a book of the repository documents on the viewer shell, with the build log under the readme | "i want prototemplate to have its own proper seo system we can also make a very small like 10 page version of the docs site that just explains how it works and totally serves as a template" | content/docs, src/app/docs, src/app/handbook/registry.ts, the redirects in next.config.ts; pnpm lint:registries, pnpm lint:seo |
Notes, commits and transcripts from July 2026 still describe these practices, and no row above names them. The Replaced column of the tables above covers the other July practices: Lenis, headings with periods, Switzer and the July bar.
| July practice | Replaced by | Date | Held by |
|---|---|---|---|
| Entrance animation, such as a fade-in on the landing hero | Kevin, 2026-08-06: "dont have a fade in animation in hero". Product pages have no entrance animation since 2026-09-28. The landing's sections keep one quiet reveal as they scroll into view, and other motion shows a real state change. | 2026-08-06; product pages 2026-09-28 | gt-motion section 1 and section 6, The landing's quiet reveal |
| Liquid glass and glassmorphism. Kevin, 2026-07-30: "MAKE THIS HAVE A BACKGROUND WINDOW DISTORTION GLASSMORPHISM" | The avoid list refuses the AI gradient and glass look. | 2026-08-11 | Review (gt-brand section 7) |
| DirectionDock, the control dock on every direction page. Kevin, 2026-07-30: "remove this thing on each page. this is a /present specific feature" | One pill took over the presenter's controls and DirectionDock was retired (Prototemplate 502dc0a). It returned the same night as a top-left back button on the standalone direction pages, hidden under ?chrome=0 (08b33c6). On 2026-09-08 DirectionCorner (src/components/viewer/DirectionCorner.tsx) replaced it on the viewer shell, and DirectionDock was deleted (c064945). | 2026-07-31; 2026-09-08 | gt-components, Prototemplate's viewer shell; prototemplate section 1 |
gt-cloud's apps/redesign on port 3006 as the place where canon was edited | Prototemplate main is the only copy, and canon is edited there. apps/redesign stopped being a copy, nothing is copied in from another tree, and Prototemplate's dev server runs on 3005. | 2026-09-08 | ARCHITECTURE.md, The mirror; docs/SHIP-LOOP.md section 7; prototemplate section 1 |
gt-voice, gt-website, gt-ship and gt-lints list the directives each skill was written from. This log puts every ruling in one dated list with the practice it replaced.skills/gt-aesthetic/references/verdicts.md and SKILL.md sections 1 and 2; skills/gt-ship sections 1 to 8, references/pr-body.md and scripts/pr-size.mjs; skills/gt-lints and references/gt-ui-rules.md; skills/gt-website sections 3 to 8 and references/docs.md and references/pages.md; skills/gt-voice, Headings and casing; skills/gt-brand sections 1 to 5 and 7; skills/gt-components; skills/gt-motion sections 1 and 6; skills/gt-dither sections 5 and 7; skills/gt-landing-pages; skills/gt-reporting section 5; skills/prototemplate sections 1 and 8 to 10; BRAND.md; DESIGN.md section 3; ARCHITECTURE.md, The mirror; LICENSE; README.md; docs/ARTIFACT-PICTURES.md; package.json; .oxlintrc.json; scripts/lint/practices.mjs; scripts/lint/type.mjs in the working tree; commits 502dc0a and 08b33c6 (2026-07-31 Pacific), 229d413 and c064945 (2026-09-08)..oxlintrc.json and tooling/oxlint-plugins/gt-ui.ts, apps/landing/src/app/robots.ts, apps/landing/vercel.json, apps/landing/gt.config.json, apps/landing/staging.gt.config.json, scripts/deploy-landing.sh, packages/node/src/database/featureFlags/isFeatureEnabled.ts, apps/admin/src/components/featureFlags/ and packages/ui/src/components/pricing/dollar-format.ts; merged PRs #4213, #4359, #4773, #4785, #4795, #4828, #4887, #4909, #5007 and #5049; open PRs #4977 and #5133.DOCS-SKILL.md ("When something gets its own top-level section" and the rule for descriptions); PR #501.redesign-v0-verdict, brand-questionnaire-directives, variants-program-state, lighthouse-round-conventions, gt-commit-identity, lost-work-audit-2026-09, agent-readable-docs, pricing-money-format, explorations-stay-local, landing-inter-only, landing-icon-rule, agent-prompt-test, pr-screenshots-and-gallery, landing-hero-agent-button, speed-marks-set, onboarding-parity-rule, pr-stacks-2026-10, artifact-picture-standard, world-language-map and prototemplate-hub-skills. Added on 2026-10-10 from an allowlist of standing rulings, each row written fresh: landing-cta-conventions, writing-for-strangers, pr-size-discipline, always-simplify and careers-team-photo.General Translation (GT) builds localization tools for developers: open-source i18n libraries, a translation platform and the Locadex agent. Prototemplate is Kevin Liu's hub for his GT work and the wiki of how he does it. It holds the brand and design canon, the brand deck, the design lab, the curated skills and the handbook, and serves all of it at www.prototemplate.com, the canonical address, and at the aliases prototemplate.vercel.app and prototemplate.kevinliu.studio. The General Translation team's Vercel project serves the first and Kevin's personal Vercel project serves the other two, and both projects deploy this repository's main branch. This file is the entry point for an agent working in this repository, or in another project that imported the hub. It is short on purpose: the skills hold the procedures and the handbook holds the depth.
Kevin's own words, 2026-10-05: "this prototemplate is supposed to be my hub for general translation work, but also i should be able to use it anywhere and build on top of it so it also acts as a wiki / repository of how i do work at GT".
The operating principles hold all eighteen with their sources.
The skills live in skills/<slug>/SKILL.md. In this repository they are already linked into .claude/skills and .agents/skills, so Claude Code, Codex and other Agent Skills loaders find them by name. skills/README.md lists them by area. The Tools column names the pnpm commands for the task, run from this checkout; docs/TOOLS.md, served at /handbook/tools, lists every command and the scripts the skills bundle.
| Task | Skill | Tools |
|---|---|---|
| Writing any text: copy, docs, captions, PRs, posts, Slack | gt-voice | pnpm lint:code (em dashes, eyebrows and heading periods in code) |
| Ending a turn, reporting state, the PR slate, decision lists | gt-reporting | pnpm check:pages --sheets (contact sheets) |
| A page, doc, route or build of generaltranslation.com | gt-website (with gt-cloud's own gt-landing) | pnpm check:pages --base <url> --pages-module <file>, pnpm lint:lines <url> |
| A landing, pricing, enterprise or careers page | gt-landing-pages | pnpm lint:lines <url> |
| Anything in this repository | prototemplate | pnpm lint:static, pnpm lint:registries, pnpm gen:all |
| Running or debugging a local server, letting Kevin try a build | gt-local-dev | pnpm dev, pnpm run doctor |
| Lag, frame cost, Lighthouse, a heavy shader or canvas | gt-performance | pnpm check:pages (layout shift per cell) |
| Design taste, polish, a surface Kevin calls bad or busy | gt-aesthetic | pnpm lint:lines:shell, pnpm lint:radius, pnpm lint:heads |
| The brand, the one Inter, marks and third-party logos | gt-brand | pnpm lint:type, pnpm build:marks |
| A slide, the brand deck or any GT presentation | gt-deck | pnpm build:deck |
| Options, directions, variants, converging on a pick | gt-explorations | pnpm capture:pages --direction <slug> |
| A lint, a gate, a new rule to enforce | gt-lints | pnpm lint:static, pnpm lint:all, pnpm lint:public |
| Proving a fix or feature is done | gt-verify | pnpm check:pages, pnpm lint:lines <url> |
| Commits, PRs, review bots, stacks, landing | gt-ship | pnpm build, pnpm build:updated --staged, pnpm lint:public |
| Workflows, subagents, briefs, handoffs, resuming after a stop | gt-orchestration | pnpm skills:usage |
| Animation of any kind | gt-motion | pnpm check:pages (interactions) |
| A blog or launch graphic | gt-graphics | none; the toolchain in graphics/ (docs/GRAPHICS.md) |
| Dithered fields and artifact pictures | gt-dither | pnpm mood-tone, pnpm lint:pictures |
| A film, trailer, promo or demo GIF | gt-films | pnpm build:motion |
| A diagram or an isometric drawing | gt-diagrams | none |
| UI components and how product UI behaves | gt-components | pnpm lint:code |
| A product surface (sign-in, onboarding, the dashboard) and reviewing its states | gt-product-surfaces | none |
docs/handbook/, also served at /handbook:
On the site the canon follows these six in the same book: this guide, BRAND.md, DESIGN.md, ARCHITECTURE.md, the ship loop, the tools, the libraries with the build log, the graphics pipeline and the artifact pictures. The docs site at /docs explains in ten pages how the hub works.
gt-voice).pnpm lint:type holds it (gt-brand).pnpm lint:lines:shell holds it.pnpm lint:radius holds it.pnpm lint:heads holds it.gt-explorations, gt-ship section 8).motion/ belongs to the Videos session and stays untracked, and nobody runs git add -A or git add . (docs/handbook/multi-session-playbook.md, prototemplate section 8).Co-Authored-By: trailer (docs/SHIP-LOOP.md section 6). gt-cloud commits carry the repository's configured General Translation work identity, which its Vercel deploys require; never set that identity globally (gt-ship section 2).pnpm install
pnpm run doctor # what this machine is missing to run the tools
pnpm dev # http://localhost:3005
pnpm exec tsc --noEmit # 3 to 5 minutes
pnpm lint:static # every lint and test that needs no server (the lane and CI gate)
pnpm lint:all # lint:static, then the live audits against PT_BASE (default 3005)
pnpm check:pages --preset quick --pages <id> # eight devices in dark and 1440x900 in light on the touched pages.next. The build gate runs in a scratch worktree with && (gt-ship section 8).playwright-core installs (pnpm exec playwright-core install chromium); CHROME_PATH overrides it.pnpm build:skills (src/lib/skills.ts, skills/README.md), pnpm build:updated (src/lib/updated.ts), pnpm build:deck, pnpm build:marks, pnpm build:tools (docs/TOOLS.md); pnpm gen:all runs every one of them. pnpm build:motion runs only when the Videos session hands a film over (gt-films section 11).README.md maps the routes, ARCHITECTURE.md the code, DESIGN.md the visual laws and BRAND.md the identity.README.md's "Import this into another project" section gives the commands. In short:
skills/ and scripts/skills/install.mjs to the project's root, then run node scripts/skills/install.mjs --project . --dry-run there and again without --dry-run. The skills land as relative links in .claude/skills and .agents/skills, the layout this repository uses.AGENTS.md, BRAND.md, DESIGN.md and docs/handbook/ beside them, so every link between the skills, the canon and the handbook keeps working, and add a CLAUDE.md that imports AGENTS.md.AGENTS.md: the project's name and purpose, its own commands and ports, its lanes, and its own gates. Keep the principles, the routing table and the house rules that still apply.A turn that changed something ends the way gt-reporting section 4 sets out: the answer to what Kevin asked in the first line, what shipped with its exact state, something he can see, a numbered list of what only he can do, and the risks, numbered so he can answer by number.
General Translation was chosen deliberately, in this order:
| name | what it is |
|---|---|
| General Translation, Inc. | the company |
| GT | the short form, and the mark |
gt | the open-source code library; you run gt translate |
gt-next, gt-react, gt-vue, gt-node, gt-python | the framework-specific packages |
| Locadex | the AI agent product |
| generaltranslation.com | the domain (also held: gt.sh, generaltranslation.ai/.dev, locadex.com/.ai/.dev) |
| glyphfield.com | the companion tooling site (shader library, animation studio) |
Every product in every language. Native-level speed and quality, from day one. That's the whole thesis, executed insanely hard.
The positioning: the Vercel of localization. Two halves designed together: open-source developer tools (the Next.js of the analogy: the gt libraries) and closed-source infrastructure (the Vercel: context-aware translation APIs, versioning, editing, integrations, agents) that is the best-in-class way to use those tools. Because we build the entire stack, we can promise what point solutions can't: consistent, high-quality translation across a whole business, integrated in an afternoon.
Two registers, one family: the open source should feel community-owned; the platform should feel enterprise-grade.
The values, which the visual identity must carry:
DESIGN.md.The brand carries itself like a "fullstack director": it writes the script and it pushes the camera. Creative and technically innovative, never one without the other. On time, under budget, over-delivering, always working with the best people, with a keen sense of the market and for making things people love.
| position | ||
|---|---|---|
| Classic | ●──── toward → | Modern |
| Reserved | ← toward ────● | Playful |
| Minimal | ← firmly | Expressive |
| Clever | ← leaning | Warm |
| Rational | ← firmly | Quirky |
| Understated | toward → | Confident |
| Serious | ← leaning | Witty |
| Neutral | ← leaning, a glint allowed | Slightly mischievous |
| position | ||
|---|---|---|
| Clean | texture only as ordered dither | Textured |
| Soft | → | Sharp |
| Geometric | ← firmly | Organic |
| Light | paper-first; dark mode is one ink surface | Dark |
| Muted | one spectral accent per page | Vibrant |
| Flat | depth from lines and material, never shadows | Dimensional |
| Monochrome | four absolute colors + one accent | Colorful |
| Structured | ← firmly | Playful |
| Warm | → | Cool |
| Elegant | ← | Fun |
These are read from the shipped system for basement to confirm or push.
Measured, declarative, precise, quietly confident. Every sentence is complete and makes one claim. A caption states the mechanism: "A framed cell has a 1px padding reveal that shows the ground underneath, so the cell draws no border." Copy uses no exclamation marks, no hedging and no marketing adjectives where a fact would do. Wit is allowed when it is precise. Technical terms are used precisely and sparingly, then explained plainly. The register sits closer to a well-written spec or a good engineering blog than to marketing copy, and the copy is about the product.
The GT monogram (/brand/gt-logo-light.svg, gt-logo-dark.svg + transparent PNG variants): every stroke of the mark is two parallel lines, the doubled-line grammar at brand scale. Locadex carries its own mark (/brand/locadex-mark.svg).
Rules:
DitheredMark), never a GIF, never a filter glow.Four absolute colors (ink #070707, raised ink #101010, titanium #8a8f98, paper #ffffff) plus exactly one spectral accent per page (the working accent: #2f5ce0; its dark-band lift #86a8ff). Structural color everywhere derives from the four as alpha steps; dark mode is a pure token remap. Full law: DESIGN.md §1. The accent is a controlled edge, never a wash; one bright white; depth from lines and material, not shadows.
The signature device: glyphs, characters that make up greater wholes. Writing systems are the raw material the brand keeps returning to:
EverySentence): a headline dissolves into glyph dust and reassembles in the next language. Matter is conserved; the same swarm becomes the next sentence.LocaleTag): flag print + code, the one way a locale is named anywhere. The prints are SVG, never emoji; a flag is a functional data chip, never decoration.All of them run live in the build log on /handbook/libraries, with their APIs.
The identity is applied on four kinds of surface today.
apps/landing. The production landing page (gt-cloud #4213) has been canonical for the site since 2026-08-11./deck) since 2026-09-25./motion apply the identity to film: the blog trailers, the product films and the translation-history series./marks) sit beside the doubled-line monogram, which stays the current mark (section 4). The title badges in the /brand and /marks heads cycle through a selection of the marks on /marks.When in doubt about how the brand behaves, read the shipped surface the work belongs to.
The site grew from the direction of the Dossier (/d/singularity-dossier). It stays in the gallery with the other directions as the working record of how we got there. From 2026-08-06 to 2026-10-07 this section named it the completed reference for the brand.
/enterprise showed Ramp with 11+ locales across its dashboard, landing page and iOS, Cursor with 14+ across web and docs, Profound with 47+ across platform and docs, Partiful with 2+ across web, iOS and Android, and Sierra with 7+ across web and GTM, with a quote from Lee Robinson, VP of Developer Experience at Cursor. /enterprise/contact had partner pages for YC, Mercury, Slash, speedrun, a16z and The Residency. Every other name stays off public surfaces, this repository included. Read the live site again before a new surface names anyone.The canon for every page in Prototemplate: the laws the directions and the shipped site run on, distilled from the founder rounds. The build log on /handbook/libraries is the living version of this document, with the diagrams drawn and the engines running. This file is the reference you read before touching a page.
Four absolute colors, declared once in src/app/globals.css:
| token | value | name |
|---|---|---|
--color-ink | #070707 | ink |
--color-ink-raised | #101010 | raised ink |
--color-titanium | #8a8f98 | titanium |
--color-paper | #ffffff | paper |
#2f5ce0; its dark-band lift: #86a8ff). One bright white. An accent is a controlled edge, never a wash..pt-root, .toolchain-root, fork roots) publishes a semantic layer — surfaces, ink steps (-2/-3/-4), hairlines (--*-hair, --*-hair-2), hatch — and everything downstream reads those.[data-theme='dark'] <root> the paper family collapses onto ink, ink flips to white, and hairline alphas rise so a 1px seam survives between two ink surfaces. One surface family in the dark, the way light mode is one white.--tc-plate), mirroring the ink / raised-ink pair dark mode runs on. --tc-panel (#101010) is the one dark artifact surface for code, config, and diffs.Structure comes from hairlines, and every line is drawn exactly once. The auditor (scripts/lint/lines.mjs) enforces it mechanically — see docs/SHIP-LOOP.md. Four defect classes:
background-clip: padding-box, everywhere.The overlap: two owners each drawing a real line along the same edge, compositing into a band darker and thicker than any line the system allows. The auditor cannot see SVG strokes — reconstructed lines come from computed CSS — so figures must be checked by eye at 2× pixel crops of the junctions.
repeating-linear-gradient(-45deg, transparent 0 6px, var(--pt-hatch) 6px 7px) between two hairlines it draws itself, border-block: 1px solid var(--pt-hair) (.pt-book-band, BookView.css). Its rules and its hatch run across the whole stage, as every rule of a book does (see The reading column), and nothing else draws a line within 4px of either rule; the first section's divider draws no rule of its own. The gallery's article (.pt-hatch, prototemplate.css) is the one place a band draws only its bottom rule, because every section there draws its own bottom rule and owns the band's top seam./directions/<slug> (.dr-sheet). The shell draws it as a 1px --pt-hair border on .sheet, a 1px paper gap from the padding of .sheet-mat, and a 1px --pt-hair-soft outline on the mat, with no shadow, on the --pt-plate letterbox of the fixed stage, as the deck's slide view draws it. A reading page has no ring and no plate. The active thumbnail frame and the active book page frame carry the same border plus offset outline. The auditor allows them under sheet, thumb-frame and page-frame.A reading page sits straight on the stage's paper. That covers the book mode of /brand, /handbook, /motion, /graphics, /skills and /marks, their record pages, /directions/<slug> and /archive/<slug>: every route that renders Sheet variant='flow'. Its column is the deck's book column (deck/parts/head.html .book-in): 1280px at most and centered in the stage, with --pt-col-pad-x (56px, 16px under 900px) on each side, --pt-title-clear over the title and 120px (80px under 900px) under the last block. The column draws no border, no ground and no mat. The toolbar's bottom rule is the only line above the title, and the sidebar's right edge is the only line beside the page. A fixed-size artifact inside a reading page keeps a frame, because the frame is its edge: the live page on a direction page keeps the fixed sheet's ring, and a capture takes the frame role with the card corner. Kevin asked for this on 2026-10-06, after the grey mat had made every page a framed picture of a page.
Every shell rule written for 900px and under also matches a phone on its side, (pointer: coarse) and (max-height: 500px), and ViewerShell.tsx isNarrow() reads the same query: a 932x430 phone gets the phone's composition and touch sizes (the list as the overlay, 44px book links) instead of the desktop list column of 28px rows. A tablet is taller and keeps the desktop composition.
Every structural horizontal rule of a book runs across the whole stage, from the sidebar's edge to the stage's right edge: the mast's rule, the contents rows, the band and the dividers, the full-width list rows of /skills, /graphics, /motion, /marks and a direction page, an archive record's rows, and the rule over a blog post's footer. The element keeps its 1px border for layout and paints it as a border image outset past both ends (--pt-bleed-top, --pt-bleed-bottom and --pt-bleed-block in tokens.css); the outset never scrolls, and the scroll region clips it at the stage's edges. A rule that drops its border also writes border-image-source: none, because the image ignores border-width and the build empties the shorthand border-image: none (lint:practices holds this). Under forced colors the hairs take CanvasText. A contents row is one line: the first link of each row draws it and the others draw none. Rules inside the content keep their measure: the head panel's rows, tables, prose lists, the --- rule, ledgers and file lists. Kevin asked for full-width rules on 2026-10-07.
Chrome is everything the viewer shell draws around content: the toolbar, the sidebar, the index panel, the search, the fixed sheet's ring, the grid, the book frame, the help card, the direction corner, and the standalone deck viewer's own chrome. Every rule in chrome is 1px, drawn once, in one of three roles, and this is part of the identity: a page reads as Prototemplate because its lines are single and their weights mean something.
The three roles, and only three:
| role | token | draws |
|---|---|---|
| structural | --pt-hair | large surfaces and the lines that divide the shell: the fixed sheet's ring, the search card, the index panel's left edge, the toolbar bottom, the sidebar right edge, the deck surface index's group headers (the sidebar's group headers draw no rule), the book head's rule, the hatch band's two rules, the section dividers, the segmented control, the field boxes at rest, the install field at rest |
| row | --pt-hair-soft | list rows, search results, panel rows, the book head's panel rows, the help card's table rows, the fixed sheet's outer ring, the progress track, the sidebar's rail (.pt-sb-rail, outline density) and the run guides (thumbnail density) |
| frame | --pt-edge | frames of images and tiles only: thumbnails, the book's page frames, the grid tiles, the 96x54 and 64x36 captures, the hover preview's frame, and the help card |
The weights carry meaning. A frame at 0.62 alpha is the darkest line in chrome because it holds a picture in place: the eye must find the edge of a capture against the paper around it. A large surface (a fixed sheet, a card, a panel, a head) is not a picture; it is the paper itself, so its edge is the structural hairline, and its outer ring (where it has one) steps down again to the row weight. A fixed sheet drawn in the frame weight reads as a boxed image, which is the bug Kevin named in round six ("make the borders around these areas the proper border colors"). The help card is the one card that keeps the frame weight: it floats over a scrim, where the hairline would vanish.
Nothing in chrome sets a border color from any other token or literal. --pt-ink appears on a border only as a state: a pressed button (.is-on), the active thumbnail or page frame (.is-active), the count while it is being edited, the solid call to action, the sidebar's current thumb on its rail, and a field while it holds focus. Book heads, table headings and the deck surface index's group headers draw --pt-hair, never ink; the sidebar's group headers draw no rule at all. Outlines are rings: the three roles, ink for focus and active rings, paper for a ring on an ink plate.
Where two bordered components touch, exactly one draws the line:
| junction | owner | the other side |
|---|---|---|
| sidebar and stage | the sidebar's right edge | the main region draws no left edge |
| toolbar and stage | the toolbar's bottom edge | the stage, the hint row and the index panel draw no top edge |
| index panel and stage | the panel's left edge | a fixed sheet's ring runs under the panel; the panel covers the progress track while open |
| group header and its first row | no one: the header draws no rule, and the group's boundary is the 16px gap above its header | the first row draws no top rule |
| a page row and its run | the group's rail (.pt-sb-rail, --pt-hair-soft), bending 45 degrees under the page row into the run; in thumbnail density the run's guide | the page row draws no bottom rule; run rows and labels draw no left border |
| a run and the deep run in it | the same rail, one more bend; in thumbnail density each run its own guide, 16px or more apart | no row draws a left border |
| the marked row and the rail | the rail; the 2px ink thumb is a state over it | the rail under the thumb is covered, not doubled; the row draws no bar |
| a row and the pills | no one: the pills are grounds and draw no line | the row draws no box |
| a page row and its fold | no one: the fold draws no border at rest, and its focus ring is a state | the row draws no right rule |
| last row of a group and what follows | the last row's bottom edge | the next header carries no top rule |
| a fixed sheet and its content | the ring (hair border, paper gap, hair-soft outline) | content draws no outer border |
| a reading column and the stage | no one: the column draws no edge | the toolbar's bottom rule is the only line above the page and the sidebar's right edge the only line beside it; content draws no outer border |
| book head and its contents | the mast's rule (--pt-hair), then the note when there is one | the note draws no rule; the contents grid draws no top rule; its rows draw their own bottom rules |
| the title and what is above it | no one | nothing is drawn in the --pt-title-clear space between the toolbar's bottom rule (the stage's top) and the h1's box, and no line crosses a title |
| book head and its panel | the panel's rows draw --pt-hair-soft under every row but the last | the panel draws no frame; side by side, the mast's rule closes the mast under the taller of lead and panel; on a narrow head the panel draws a row rule over its first row and the mast's rule is its last row's rule; the install field draws its own --pt-hair box and the row above it drops its rule |
| front matter and the first section | the band, both rules | the contents grid draws no rule against it; the first section's divider draws no rule |
| two sections | the lower section's divider rule | the upper section draws no bottom rule |
| tile and its shot | the tile's frame | the shot draws no border |
| segmented control and its options | the control's outer border | options draw no dividers; the active option is an ink pill 2px inside it with the chip corner, and the hover and the focus ring trace the same pill |
| stacked corner buttons | the upper button's bottom edge | the lower button's top edge is transparent at rest |
| sidebar head and toolbar | each owns its own side of the vertical seam | the two bottom rules meet at the sidebar's edge and never overlap |
The auditor enforces this from computed CSS. pnpm lint:lines:shell walks /, /brand, /compare, /archive/<first slug>, /directions/<first slug>, /skills, /skills/<first slug>, /handbook, /motion, /motion/<first package>, /graphics, /marks, /d/production and /deck (the deck's own document) at 1440, 1280 and 390 in both themes against the dev server on port 3005, with the list toggled, the index panel open, the search open, and the grid and book modes on / and /deck. It fails on any doubled line (two owners within 4px), any junction (two owners coincident on one seam), and any border color in chrome outside the three roles. The sidebar's rail is a masked box the walk cannot see, so the auditor reads its vertical runs from its path (data-rail-path), holds its ink to the row role, and fails a state audited with the list open in outline density when the rail layer is not live. pnpm lint:all runs it. The allow list in scripts/lint/lines.mjs names every sanctioned multi-stroke device with its reason inline; for chrome those are the fixed sheet's ring (sheet: a hair border, a paper gap, a hair-soft outline, on /compare, in slide mode and around a direction's live page), the active frames (thumb-frame, page-frame, the sidebar's site tiles as pt-tile) and the hover preview's mat (pt-preview).
Scroll regions belong to the same law: one thin scrollbar everywhere in chrome, a 4px gutter with no track rule and a 2px thumb in --pt-thumb that widens to 4px on hover, owned by .pt-scroll in src/components/viewer/tokens.css and mirrored by the deck's .scroll rule. No scroll region draws a rule beside its thumb.
Kevin's toolbar is the reference (2026-10-05): the search pill and the segmented control round at 6px, the key chip at 4px. Everything a reader presses, types into, picks up or looks at as an object is rounded; the surfaces that hold the interface are square. src/components/viewer/tokens.css holds the corners as six tokens, and nothing in the shell or the pages writes a radius any other way.
| class | token | value | members |
|---|---|---|---|
| shell | --pt-radius-shell | 0 | the viewer frame, the sidebar column, the toolbar bar, the fixed sheet and its ring, the reading column, the index panel's column, the overlay layers and scrims, the progress track, scroll thumbs, the book head, the contents, the dividers and the bands; full-width list rows, which own their seams and never draw a box; an input inside its field |
| control | --pt-radius-control | 6px | every button (.pt-ib), segmented groups, fields and field-shaped buttons (the search pill, the count, the filters, the palette's field, the install field), buttons on pages, the sidebar's pills, which are grounds the pointer is on |
| inner | --pt-radius-inner | 5px | a part flush inside a control's 1px border: the install field's copy segment |
| chip | --pt-radius-chip | 4px | key caps, chips, pills, tags, inline code, a segmented control's active and hover pill (2px inside its option), and a control set 2 to 7px inside a field |
| card | --pt-radius-card | 6px | cards, tiles, thumbnails, figures, and popovers: the palette, the help card, the toast, the hover preview |
| round | --pt-radius-round | 50% | dots and avatars on a square box |
calc(var(--pt-radius-<role>) - npx).scripts/lint/radius.mjs holds the law: statically in pnpm lint:radius and the build, and on the rendered pages in pnpm lint:radius:live.The corner notches on hero cards are not drawn — they are the ground showing through, so a corner can never disagree with the seam that meets it.
The page's spine is a ruled column with ONE rail on each side.
border-inline.-in column, in the band's rule ink. It never draws a pair beside the column.The doubled LINE of section 5 is a different device: one path stroked twice, a connector, never a page rail.
The sidebar's rail (section 16) is a different device: a row-weight line inside the list that marks the place, never a page rail.
letter-spacing: 0.06em), the mono for numbers and tokens only.The shell's type is one family, the rsms InterVariable (v4.1), cut into unicode-range subsets by scripts/build/subset-inter.py (pnpm build:inter): Google Fonts' latin, latin-ext, vietnamese, greek, greek-ext, cyrillic and cyrillic-ext, plus Latin Extended-A, the private use area and the remaining symbols, each with every feature and both axes. The roman's latin subset comes through next/font, bound as ptInter in src/lib/fonts.ts so its family name matches no installed Inter, and every route preloads it. The other subsets and the italic are plain @font-face rules in the same ptInter family in src/app/inter-subsets.css, each fetched only where the page draws a code point in its range. Any other localFont call sets preload: false (pnpm lint:type, T3). Every stylesheet reads the type tokens of src/components/viewer/tokens.css and declares no family, feature list or display size of its own. The /d/ directions keep their own type.
--pt-text is var(--font-inter), system-ui, sans-serif; --pt-display reads it. --pt-mono is for code, numbers and tokens only. The per-language stacks (--pt-text-hant, -hans, -ja, -he) name system faces after Inter. The nameplate's faces are --pt-face-serif and --pt-face-grot..ptc-every) take --pt-ff-display: the single-storey a (cv11) and the open digits (ss01). Everything else takes --pt-ff-text. Both name 'liga' 1, 'calt' 1, because Chrome turns contextual forms off under any letter-spacing. The base rules on the shell's roots set them; a rule never writes a feature list.strong. Nothing above 500.| step | role | size / line | tracking | 900px and under | | --- | --- | --- | --- | --- | | d1 | page title (the book head's h1) | 44 / 1.04 | -0.025em | 32 | | d2 | section title (a divider's h2, a brand chapter) | 32 / 1.05 | -0.025em | 26 / 1.15 | | d3 | h2 in prose, a record's title | 24 / 1.25 | -0.02em | 21 |
--pt-measure (32em, about 66 characters); a head's lead at --pt-measure-lead (30em). Tables, figures, code and ledgers keep the full column.BookHead (BookView.tsx), and differs from another only in its words and its facts: 1. nothing is drawn in the --pt-title-clear space between the toolbar's bottom rule and the title (the reading column's top padding), and no line crosses a title; 2. the title, the page's plain name from src/lib/page-names.ts (Brand, Documentation, Motion) or a record's own title, at d1 across the mast; after the name, a badge on the title's line (BookHead's badge: a 1px --pt-hair box at the card corner, its content at most the cap height tall and set on the baseline, hidden from the accessible name) cuts every 1.4s through what the page holds (src/components/viewer/BadgeCycle.tsx, steps(n) on one strip, so the count lives in the frames; under reduced motion the first frame stands): /brand the GT monogram and the register's monograms, /marks the register's marks, /handbook its files, /skills the skill ids, a skill its files, /graphics the numbered visuals' file stems, /motion the film slugs, a translation film its vocabulary in its own script, /blog the post slugs, a direction its pages; 3. the lead (one to three lines at --pt-measure-lead, at most 200 characters, plain declarative sentences) and the panel on one row under the title, both starting on the lead's first line; 4. the panel: Updated first (the day of the last commit that touched the page's own sources, from src/lib/updated.ts, linked to its commit, with a relative hint after hydration), then three facts, or one fact and the install field; each row a Heroicons 20 solid glyph in titanium, a label in ink-2 and a value in ink at 500, one lead line tall with its text centered between 2px of air above and below, and a --pt-hair-soft rule between rows, none over the first row or under the last; beside the lead the panel starts 2px under the lead's top, so each row's baseline lands on a lead baseline; 5. one --pt-hair rule --pt-head-rule-pad under the taller of lead and panel, across the stage; 6. the note at the body step, then the contents; 7. one hatch band ruled on both edges and running across the stage, --pt-sec-over under the block above it; 8. the sections, each a section.pt-book-part opened by a divider: a --pt-hair rule (the first section's rule is the band's), --pt-sec-pad, a gutter note of two lines in titanium (Section n, then one fact) on the h2's last baseline, the h2 at d2.Under 880px of head width the panel stacks under the lead, ruled the same way, and the mast's rule sits --pt-head-rule-pad under it. The gallery (/) is the site's front page, the nameplate hero and the redesign post in the article's own grammar, and has no book head; /compare is a tool and has none. scripts/lint/heads.mjs holds the structure, statically and on the rendered pages.
scripts/lint/type.mjs holds all of this: statically in pnpm lint:type and the build, and on the rendered pages with pnpm lint:type:live. Its allowlist names every exception with its reason: the nameplate, the gallery's grotesk labels, the mono numbers and tokens, the specimens, and other sessions' code. scripts/lint/heads.mjs holds the book page (pnpm lint:heads, pnpm lint:heads:live).The brand's connector: one SVG path stroked twice — a full-gauge ink stroke under a narrower surface-colored core (stroke-width: gap), carving two parallel hairline threads at a constant gap along any curve. Tokens: --thread-gauge: 1.5px, --thread-gap: 3px.
vector-effect: non-scaling-stroke.stroke-dasharray — dash distances drift under anisotropic stretch.src/components/shared/diagrams/DoubledLine.tsx. Offset clones collapse on curves; concentric restrokes only make symmetric rings.One 30° axonometric map for every iso drawing (src/app/d/toolchain/diagrams/iso.ts): project(x, y, z) = [(x−y)·cos30, (x+y)·sin30 − z], camera at (+,+,+).
roundedPolygon(silhouette(...)) (the occluder), then right/left/top face fills, then hairlines — rim, top contour, and the interior front edge(s) the silhouette doesn't draw.IsoPrism extrudes any convex plan polygon under the same visibility and tone law; plane(z, ox, oy) seats whole flat drawings into a surface with one matrix; markPath() lays rounded bars in a face; brand marks render as alpha masks so the shape takes the surface's ink.ISO_RADIUS = 2.4); plate thickness ≈4% of footprint, air between stacked plates ≈40%; depth cues live in per-plate stroke alphas, never group opacity; each drawing spends its accent on exactly one element.DitheredMark (§7's 1-bit language as a specular band): nested Bayer coverage tiers windowed by pre-rotated 60° clip paths, swept by pure horizontal translate — rotate() windows are GSAP-origin fragile.Density ramps render as ordered dither, never alpha veils.
[0,8,2,10 / 12,4,14,6 / 3,11,1,9 / 15,7,13,5]) and the 8×8 (an exact permutation of 0..63 — 65 tonally linear levels) are the house screens.crispEdges keeps cells 1-bit at any zoom; CSS upscales with image-rendering: pixelated.src/lib/dither.ts (CPU, any scalar field, 1 device px per cell) and src/lib/studio-field.ts (GPU, the authentic BAYER_PRESETS roster of ten variants, one shared GL context, switch by remount).docs/ARTIFACT-PICTURES.md, held by scripts/lint/pictures.mjs.lang + dir on the node; unicode-bidi: isolate on the container.fonts.ready.src/components/shared/EverySentence.tsx.LocaleTag): flag first (fixed 15×11 SVG print), code in the host's mono on the baseline; the host supplies the box.prefers-reduced-motion short-circuits setup entirely; the markup pose IS the still (static accent pair, parked seam cut, single dither frame).pathLength to 1000 (GSAP integer-rounds offsets); dashes clip at a subpath's end rather than wrapping closed loops; Chromium computes dash patterns in screen space under non-scaling-stroke and ignores pathLength there — rings that dash use raw user units with neither.d (el.dataset.traceD) before an animation blanks it per tick — a re-run would otherwise trace an emptied path.beamAt(t)), never a translated constant.--pt-dur-sb (a pick) and --pt-dur-toast (the pointer), eased by --pt-ease. Their geometry is read once per layout change (a ResizeObserver per group, a MutationObserver for rows and marks), never per frame and never on hover. Before the shell settles they are placed, not moved. Reduced motion places them at once.Both layers full-width and pinned; the handle writes one CSS var (--seam-cut) and the top layer clips to it — content never travels with the handle. State lives in the var: drags cause zero re-renders, GSAP tweens drive the same dial, the keyboard path re-reads the live computed value. The handle is the dossier refit: a 64px transparent hitbox, solid two-tone threads with the payload's ink filling the run, and a 16×40 rectangular grip tab — the light surface wears the dark chip, the dark surface the light chip. Component: src/app/d/toolchain/sections/RevealSeam.tsx (host CSS supplies the skin).
Every canvas/GL engine follows one contract: mount lazily (an IntersectionObserver arms the plate), pause offscreen and on hidden tabs by its own observer, render exactly one still under reduced motion, and destroy() releases everything the instance owns (shared GL contexts persist for the session by design). Ink re-resolves on data-theme flips. Where an engine has quality tiers, quality follows measured frame cost, not just the window: glyph-field's frame-time governor ratchets a slow device down the phone cut's own ladder (lean device-pixel cap, then the lean pool), down only, with pool cuts applied at idle.
Under 720px, type and spacing are one token ladder, not per-rule values — --tcm-* slots declared on .toolchain-root in the engine's LATE 720px block (src/app/d/toolchain/styles.css), consumed by every floor below:
| slot | value |
|---|---|
--tcm-h2 / -h2-lh | 2.25rem / 1.18 |
--tcm-h3 / -h3-lh | 1.375rem / 1.3 |
--tcm-h4 / -h4-lh | 1.125rem / 1.35 |
--tcm-lead / -lead-lh | 17px / 1.55 |
--tcm-body / -body-lh | 16px / 1.6 |
--tcm-small / -small-lh | 14px / 1.55 |
--tcm-kick | 13px |
--tcm-quote / -quote-lh | 22px / 1.4 |
--tcm-head-pt / -head-pb | 72px / 36px |
--tcm-gap-h2 / --tcm-gap-h3 | 18px / 14px |
--tcm-cell-pad | 28px |
--tcm-box-pt / -box-pb / -box-gap | 36px / 38px / 28px |
var(--tcm-X, <px fallback>) with the slot's canonical value as the fallback — a rule the ladder doesn't reach (a 721–900px cut, a fork that hasn't declared it) degrades to the same number, never to unstyled. New mobile rules never hardcode a size the ladder has a slot for.@layer, so engine floors outrank utility metrics (the box-gap rule beats a head's max-lg:mb-6). Heading and paragraph metrics therefore live in engine CSS, never in page utilities.v0-pages.css's mobile cut (.toolchain-root:is(.sgdh-root, …)) carries higher specificity than any engine floor, so it must consume the same tokens — an engine-only raise silently fails to render on the singularity homes. Change one, change both.Boxed cells — framed cards, bento plates, stacked dark-band cells — breathe alike on mobile: block padding --tcm-box-pt/--tcm-box-pb (36/38), and --tcm-box-gap (28px) between a box's head and its artifact. The gap is card-scoped (.tc-card > .shell-cell-head) on purpose: cells are flex columns, margins never collapse, so an unscoped head margin would stack onto the copy cells' own body margins. The seam floor rides alongside: copy never sits within 20px of a hairline (--tc-card-pad: 20px; the trust lead's 20px bottom pad).
A mobile stage measures the viewport twice, and the two units never trade jobs:
100dvh, the real viewport with browser chrome collapse included.100svh, the stable viewport: a dvh layout height grows the document on every chrome toggle and jitters the scroll under it.--v0sm-bar-gap (dvh − svh, 0 while the URL bar shows) — and only overhangs (the rails' lower reach) spend it. Anything SIZED from the stage derives from the stable var, or it breathes with the URL bar.The stack story runs on two ladders of scroll anchors, measured per beat and re-anchored on every ScrollTrigger refresh (src/app/d/_v0/sections/FullStack.tsx):
One scrubbed dial spans the whole read; a piecewise map (measured lock-ins in, story time out) HOLDS the clock while a row is being read and spends each gap hold → build → lock, so a layer stands locked before its own copy takes the line.
The shell's chrome is Inter only, weight 500 or less, corners as section 2 sets them (square shells, 6px controls and cards, 4px chips), one hairline per rule, colors from src/components/viewer/tokens.css and borders in the three roles of section 2. Kevin's round six directives override those rules for exactly five elements. Each exception is listed here with its owner file so nothing else reaches for it, and so the next sweep does not "fix" it back.
| element | exception | owner |
|---|---|---|
| Search pill (toolbar) | hover border --pt-ink-2, a fourth border color in chrome, reachable only under the pointer (the line auditor never drives hover). At rest the pill stays in the hair role, and while its palette is open (aria-expanded="true", a state the auditor does drive) it draws --pt-ink, the active-state color every open field takes | Toolbar.css, .pt-toolbar .pt-search-btn |
| Search key chip | kbd.pt-search-kbd: ground --pt-hair-soft, 11px weight 500 titanium; reads ⌘K, and Ctrl K on Windows and Linux (Search.tsx swaps the text after mount from the user agent) | Toolbar.css, Search.tsx |
| Present | the one .is-solid button: border-radius: 8px (the one named corner exception, hatched in lint/radius.mjs), 14px sides, the label first and the 12px play glyph after it (flex-direction: row-reverse, so ToolButton keeps one markup), hover drops the ink ground for ink text in the ink frame, the old .pt-nav-present from 430e3c7 | ToolButton.css, .pt-ib.is-solid; Toolbar.tsx passes solid |
| Prototemplate mark | the rainbow core: five chroma stops (#4b3bff, #00b3ff, #27d17e, #ffc53b, #ff3b6b, display-p3 where supported) declared as --pt-mark-c1 to --pt-mark-c5 on .pt-mark, the one color in chrome outside the site icons; hovering the head link (or any a or .pt-mark-host around the mark) fades the hatched paper fill in over the core, opacity only, over --pt-dur-enter (200ms, 0 under reduced motion). The markup is the old nav's span with four i.pt-mark-line and i.pt-mark-fill | PtMark.tsx, PtMark.css |
| Sidebar nameplate | the name beside the mark on every Prototemplate route is span.pt-brand-word with b.pt-face-serif proto in Fraunces 600 and b.pt-face-grot template in Space Grotesk 500 at 14.5px, two faces outside Inter and a weight above 500; the fonts load from src/lib/brand-fonts.ts with their variable classes on the span, so /handbook and /brand carry them too. The deck keeps its Inter title beside the GT mark | Sidebar.tsx (head), Sidebar.css (head rules) |
Everything else in chrome keeps the rules above: every other toolbar button takes the control corner, the palette and the help card take the card corner, the index panel is a shell column and stays square, and no other element carries a chroma or a face outside Inter.
Three rules govern the list in column one (src/components/viewer/Sidebar.tsx), on every route that shows the site map.
Every row is a link to a page. A site map row navigates to its route through the router; a row that leaves the site opens a new tab. A route's own item navigates too when its href names a page other than the one the reader is on: a direction row on / opens /directions/<slug>, an archive row /archive/<slug>, a skill row /skills/<slug>. The shell selects in place only an item with no address of its own (a slide, a brand section), an item that asks for it (ShellItem.inPlace, the documents on /handbook, whose book scrolls and writes the address itself), or the item of the page the reader is already on. No row scrolls the gallery. The grid's tiles and the filter's Enter follow the same rule.
A page's sections open under its row. On the page's own routes, its sections open as a run under the page's row, in the row's group (ShellSection.under: brand, docs, handbook, marks, graphics, motion, skills). A run is flush under the row. Each row of the run draws the route's own number in the icon's slot, in the deck's number style, and a name that wraps (a run row to two lines at most, a deep heading to three) and is never cut; a title that is a sentence takes a short name (ShellItem.short). When two or more of the page's sections hold more than one row, each gets a label with a chevron and a count inside the run; a section of one row is that row. The headings of the item being read hang under it as a deep run, at 12.5px titanium, ink while read. A chevron button at the right end of the page row folds the run for the visit; the run opens again on the page's next visit. A labelled group opens when the run has 40 rows or fewer, and past 40 only the group holding the active item opens; those folds persist under gt-shell-groups. A route section that stands for a site map group (Shipped, Sites, Explorations, Archive) keeps replacing that group. The sidebar is 208px, the deck's column.
The rail marks the place. In outline density each top-level group carries one rail: a 1px --pt-hair-soft line drawn by .pt-sb-rail (src/components/viewer/SidebarRails.ts) and masked to an SVG path down the group's rows. The rail runs in a column 16px before each level's first glyph: x 18, under the group header's chevron, for the tree rows; x 34 for a run's rows and labels, a site's enterprise page and the live site's rows, whose icons and numbers start at 50; x 50 for the deep headings, which start at 66. Where the level changes, the path bends at 45 degrees across one 16px step. The marked row carries a 2px ink thumb on the rail and a --pt-sb-pill-current ground with the control corner. The row under the pointer, or under keyboard focus, carries a --pt-thumb thumb and a --pt-plate ground. The marked row is the deep heading being read when the deep run is shown, else the active item when its row is shown, else the current page's row, else the page row the reader is inside. A plain click moves both marks to the clicked row over --pt-dur-sb and turns it ink before the page arrives. When the click opens another route, the new page's list keeps the clicked row where it was and the marks travel from it to that page's mark once the shell settles. Thumbnail density has no rail: its rows lead with their captures, each run keeps its guide and the active row its 2px bar.
The current page is always marked. The row whose path is the longest one covering the pathname is current, site map row or route item alike: on /skills/<slug> the skill's row, on /directions/<slug> the direction's, on /d/production/enterprise the Enterprise row and not Home. While no item is marked (a direction page, the gallery at its top) that row carries the rail's thumb and ink text; while an item is, it draws ink text (.is-current, aria-current="page"), as does the page row the reader is inside (Skills on /skills/<slug>). Exactly one row is aria-current="page": the current row whose own path is the pathname; a page row the reader is inside is "true".
The list is cheap to change. Rows are memoized on data built once per route, so a fold renders only the rows it adds or removes, a pick renders the rows whose state changed, and a hover renders nothing in React. Rows are opaque in their first frame; nothing fades in on mount. A row that opens another route prefetches it after the pointer rests on it for 80ms or on keyboard focus, never on pointer down: the click's own navigation follows within milliseconds, and a prefetch there fetches the route twice and renders the arriving page twice. The index panel builds its rows on first open.
How Prototemplate is put together, and the rules that keep its twenty-seven directions coherent. Read DESIGN.md for the visual laws; this file is the code map.
src/
app/
page.tsx the index: the design lab (GalleryViewer), the
anatomy wall and the capabilities ledger
layout.tsx the root layout and the pre-boot theme script
d/<slug>/ one route per direction (src/lib/directions.ts)
d/toolchain/ THE SSOT: sections, diagrams, styles other forks import
d/_v0/ shared v0 sections (TranslateWindow, StackTower,
Locadex, FullStack, Deploy) used by the
singularity-* homes and the shipped site
d/production/ generaltranslation.com as it shipped, rebuilt
page for page, with the plate pages
directions/ /directions/<slug>: each direction as a book
page, with its live frame (DirectionFrame)
archive/[slug]/ the retired directions, kept as captures
(src/lib/archive.ts)
compare/ /compare: two directions in synced frames
present/ the presenter deck (intro, prototypes, scoreboard)
brand/ /brand: the brand book
docs/ /docs: the docs site, fumadocs' notebook layout
outside the viewer shell; layout.tsx, the one
page route [[...slug]] over content/docs (MDX,
read through src/lib/source.ts and
source.config.ts) and docs.css, the adapters
to the site's tokens
handbook/ /handbook: the handbook's registry (its own
documents and the canon), its two routes, and
libraries-book.ts, which appends the build log
to the libraries document
md/, llms.txt/, the agent files: the Markdown twins of every
llms-full.txt/, docs page and handbook document (reached at a
AGENTS.md/ page's address plus .md), /llms.txt,
/llms-full.txt and /AGENTS.md, generated from
the registries
api/search/ the docs site's static search index
craft/ the build log after docs/LIBRARIES.md on
/handbook/libraries: laws, auditors and live
library plates (libraries.ts); /craft redirects
there
skills/ /skills, /skills/<slug> and the raw skill files
marks/ /marks: the mark explorations
blog/ the docs-redesign posts (content/blog) rendered with
the landing site's MDX components
graphics/ the illustration catalogue, read from
graphics/build/manifest.json
motion/ /motion and /motion/<slug>, from src/lib/motion.ts
api/try/fetch/ the one runtime function, behind the shipped /try
prototemplate.css the pt grammar (index + craft chrome)
globals.css the four color tokens
components/
viewer/ the viewer shell every route except /deck,
/present, /blog, /docs and /d runs on: ViewerShell,
Sidebar, Toolbar, Sheet, BookView, GridView,
IndexPanel, Search, PreviewLayer, tokens.css
shared/ cross-page instruments: EverySentence, StudioField,
PrismaticField, HeroFieldSwitcher, TcMobileNav,
diagrams/ (DoubledLine and the older line-art
set); FeatureBento, StorySection and
LanguageWheel are unmounted references
blog/ the blog's MDX components (PostAuthors, PostCover,
Carousel, AuthorSpotlight and the figures)
try/ the report card the shipped /try page mounts
book/ the handbook's book: BookShell (the shell),
book.tsx (reads and renders the documents on the
server), markdown.tsx and links.ts (the renderer
and where a repository path opens on the site)
docs/ the docs site's parts: the sidebar footer, the
theme button, the page actions, the sidebar
motion copied from gt-cloud, and pages.ts, the
ten pages as the hub's registries list them
shell/ Bento primitives (Rails / BentoRow / BentoCell)
plate/ the dashboard's sign-in and onboarding system
(frame, field, pages, fixtures) with its state
console, served under /d/production/{signin,
onboarding,consent,device,cli}
lib/ the registries: surfaces.ts, search-index.ts,
shell-data.ts, directions.ts (the direction
registry), archive.ts, marks.ts, page-names.ts
(each page's plain name); the engines: dither.ts,
studio-field.ts, glyph-field.ts, horizon-field.ts,
prismatic-field.ts; try/ (the report card's
checks); and the generated skills.ts, motion.ts
and updated.ts (build/updated.mjs; server modules
only) with page-updated.ts (its type); the hooks:
use-mount-effect.ts and use-layout-work.ts
(dependency layout effects with useGSAP's timing,
without GSAP)
scripts/ pnpm names are the interface; every entry point
takes --help
lib/root.mjs the repository root every script resolves from
lib/site-pages.mjs page discovery, the theme door, PT_BASE and the
Chrome the browser scripts share
lib/help.mjs --help: prints a script's opening comment
build/deck.mjs deck/ to public/brand-deck.html and
public/shots/deck (pnpm build:deck); next.config.ts
rewrites /deck to the built file
build/thumbs.mjs the 640x360 previews in public/shots/thumb, cut
from the captures under public/shots
build/skills.mjs skills/ to src/lib/skills.ts and skills/README.md,
with the skill contract and lint (pnpm
build:skills; --check is pnpm lint:skills)
build/updated.mjs git to src/lib/updated.ts, the day each book page
last changed for its head's Updated row
(pnpm build:updated; --check is pnpm
lint:updated, in the build), with its tests
build/speed-marks.mjs the speed marks under public/marks
build/subset-inter.py the Inter roman and italic to unicode-range
subsets in public/fonts/inter, their faces in
src/app/inter-subsets.css and the latin range in
src/lib/fonts.ts (pnpm build:inter)
build/motion.mjs motion/ to src/lib/motion.ts and public/motion,
run only when the Videos session hands a film over
check/capture-pages.mjs the 1440x900 page captures in public/shots/pages
that build/thumbs.mjs cuts
check/gallery-shoot.mjs the anatomy wall's tiles (see "The gallery
pipeline")
check/pagecheck/ the page check: every page at ten viewports in
both themes, layout shifts, interactions, a
report (pnpm check:pages; its README explains)
lint/lines.mjs the line auditor (see docs/SHIP-LOOP.md)
lint/practices.mjs the practices ratchet (+ baseline JSON)
lint/type.mjs the type lint: one Inter through the tokens,
static (lint:type, in the build) and --live
(lint:type:live), with its ratchet baseline
type.baseline.json and its tests
lint/radius.mjs the radius lint: rounded controls, square shells,
every corner through the six --pt-radius-<role>
tokens, static (lint:radius, in the build) and
--live (lint:radius:live), with its tests
lint/heads.mjs the book page lint: one head, one band, one
divider grammar on every book, static
(lint:heads, in the build) and --live
(lint:heads:live), with its tests
lint/pictures.mjs the artifact picture standard
(docs/ARTIFACT-PICTURES.md), in the build
lint/shell.mjs token discipline for the shell layer
lint/registries.mjs registry drift: headings, route lists, first
slugs, the skill contract (pnpm lint:registries)
lint/oxlint-plugins/ the gt-ui rules pnpm lint:code runs
media/mood-tone/ the artifact picture cutter and standard.json
skills/install.mjs links or copies skills/<slug> into a project's
.claude/skills and .agents/skills (pnpm
skills:install; its tests: install.test.mjs,
pnpm test:skills)
docs/
handbook/ how Kevin runs GT work, served at /handbook:
the operating principles, the quality bar, the
multi-session playbook, the product map, the
glossary and the decisions log, with README.md
research/, reference/, reference-shots/, composites/
the exploration archive the skills cite;
nothing here is served
archive/ finished round specs kept as history, with a
README naming each one; nothing here is served
deck/ the deck source: parts/, slides/, fonts/, shots/
graphics/ the blog-illustration toolchain (docs/GRAPHICS.md)
content/ the three docs-redesign posts and their authors
public/ what the site serves: brand-deck.html, shots/
(captures, thumbnails, the gallery tiles), brand/,
marks/, media/ and motion/ (films), static/
(blog images and avatars), fonts/, logos/
skills/ the curated skills, one folder per skill
(SKILL.md, references/, scripts/); the canonical
copy, published on /skills
.claude/skills/, .agents/skills/
relative links to skills/<slug>, made by
pnpm skills:install --project .
motion/ the films, untracked, owned by the Videos session
AGENTS.md the agent entry point (CLAUDE.md imports it);
served at /handbook/agents and raw at /AGENTS.md
content/docs/ the docs site's ten MDX pages and meta.jsonsrc/lib/directions.ts is the single source of truth for what exists: twenty-seven directions, four of them full site pairs (site: true): singularity-dossier, singularity-orbit, singularity-signal and production, the shipped site. singularity-dossier is the completed concept; signal and orbit keep their own heroes and carry the previous-generation sections the dossier retired, as exploration showcases. The index and the sitemap map over DIRECTIONS, and the presenter walks its first 16 without Signal (src/app/present/directions.ts), so adding or removing a direction there updates all three. When a direction is deleted, also sweep scripts/lint/practices.baseline.json for its paths, record it in src/lib/archive.ts, and re-check stated counts (index funnel, layout description, craft intro).
src/app/d/toolchain/ is the single source of truth for the tc-* vocabulary. The fork homes (chroma-flow, dither-field, aurora-paper, glyph-rain, prism-light, lens-gate, paper-foundry, terminus-board, wide-rule, event-horizon, hourglass, singularity and the rest) import toolchain's sections and diagrams directly and re-skin by root-class rescoping: each fork carries a root class (.lensgate-root, .terminusboard-root, and so on) and copies only the CSS it must re-scope.
glyph-rain/diagrams/lang/* among them). Before adding a new copy, import the SSOT and rescope CSS instead.toolchain-root sgXh-root) and pull shared v0 sections from src/app/d/_v0/; their /enterprise pages are singularity-based. Never edit toolchain's TopNav for one site.The signature pieces live as libraries, outside page code. Each has a live plate and an API snippet in the build log on /handbook/libraries (src/app/craft/libraries.ts):
| module | what it is |
|---|---|
src/lib/horizon-field.ts | WebGL lensing black hole |
src/lib/glyph-field.ts | canvas glyph rain (drift, copy modes, matter-conserving morphs) |
src/components/shared/PrismaticField.tsx | the chroma wash |
src/lib/dither.ts | CPU 1-bit Bayer field renderer + field factories |
src/lib/studio-field.ts | GPU Bayer family, the BAYER_PRESETS roster |
src/app/d/toolchain/diagrams/iso.ts | the isometric kit (boxes, prisms, plane, markPath) |
src/app/d/toolchain/diagrams/DitheredMark.tsx | masked logo + Bayer shimmer |
src/components/shared/diagrams/DoubledLine.tsx | the two-thread stroke (two-tone capable) |
src/app/d/toolchain/diagrams/EdgeGlobe.tsx | the delivery globe |
src/app/d/toolchain/components/LocaleTag.tsx | the locale pill |
src/app/d/toolchain/sections/RevealSeam.tsx | the slide-to-reveal seam |
src/components/shared/EverySentence.tsx | the sentence-rewriting morph |
When a page needs one of these behaviors, mount the component and do not re-implement it locally. When an engine gains an option, update its build log entry (body and snippet) in the same round.
The index's anatomy wall is fed by one shooter, and the file names are the contract between the two ends:
scripts/check/gallery-shoot.mjs) shoots the flagship home section by section across desktop and mobile cuts and both themes. It takes element screenshots anchored on each section's own landmark selector, never scroll depths, so side-by-side pairs align at any viewport. An addInitScript writes localStorage['gt-theme'] before first paint, which the root inline script applies, and one full scroll pass settles every lazy section before the first shot. A selector that misses is reported and skipped.manifest.json beside the tiles, { flagship, generatedFor, sections: [{ key, label, cut, theme, file }] }. Nothing reads it today; it records what the last run shot.src/app/AnatomyWall.tsx) reads: sec-<key>-<cut>-<theme>.jpg under public/shots/gallery/ (key is hero, customers, story, developer, locadex, context, global, deploy or footer; cut is desk or mob; theme is light or dark). The wall accepts a .png under the same stem, and a missing tile leaves hatched ground. The var-* tiles still in the folder are the retired directions' last dark captures; nothing renders them./compare puts two directions side by side as synced same-origin iframes. Same origin is what lets the route drive both frames' scroll and theme together. src/app/SiteCompare.tsx is the still-image version: two captures under the house seam, the cut held in one CSS variable.AGENTS.md: the entry point for an agent, here or in a project that imported the hub (served at /handbook/agents; CLAUDE.md imports it).BRAND.md: the identity canon (the basement-facing brand book; served at /handbook/brand and /brand).DESIGN.md: the visual canon (this repo's law book).docs/SHIP-LOOP.md: the verify/ship procedure every round runs.docs/LIBRARIES.md: the library index (defers to the build log after it on /handbook/libraries for depth).docs/GRAPHICS.md: the graphics pipeline: how the blog illustrations are made with graphics/, and the rules that came out of review.docs/handbook/: the handbook, what spans the skills (principles, the quality bar, the multi-session playbook, the product map, the glossary, the decisions log). src/app/handbook/registry.ts lists it with the canon after it (AGENTS.md, BRAND.md, DESIGN.md, this file, the ship loop, the tools, the libraries, the graphics pipeline and the artifact pictures), and /handbook renders the whole book through the book shell (src/components/book/BookShell.tsx). A relative link in any rendered document resolves the way GitHub resolves it (src/components/book/links.ts): a rendered document opens its route, a skill its page, any other file GitHub.content/docs/: the docs site, ten MDX pages on how the hub works, rendered at /docs by fumadocs (source.config.ts, src/lib/source.ts, src/app/docs, src/components/docs). /docs/template explains how to lift it into another project.src/lib/seo/: the SEO system. Every route's head comes from one seoRoute() call under the policy in policy.ts; /docs/seo explains it.skills/<slug>/ is the one copy of each curated skill: SKILL.md with its frontmatter contract (name, description with "Use when", metadata title, areas, updated and origin), its references and scripts. scripts/build/skills.mjs validates the set and writes src/lib/skills.ts and the folder's index, skills/README.md; /skills and /skills/<slug> render it, the pages read each body from the folder on the server, and src/app/skills/[slug]/[...path]/route.ts serves the raw files.scripts/skills/install.mjs links or copies the set into any project (--project <dir>, --user, --into <dir>, with --dry-run). This repository's .claude/skills and .agents/skills are its relative links (--project .); README.md's Skills section has the rules.Prototemplate main has been the only copy of this code since 2026-09-08, when gt-cloud's apps/redesign stopped being one, so nothing is copied or rsynced into it from another tree (docs/SHIP-LOOP.md section 7).
The verify-and-ship procedure every round of work in Prototemplate runs before it lands on main. Each check below must pass: the line audit, the page check, the ratchets, the type checker, the captures and the gated build.
http://localhost:3005 in the shared checkout. A worktree runs its own server on another port, and PT_BASE points the live lints, the page check and the captures at it.git status before staging; commit only your own files. Expect the other session to absorb your changes into its commits. When that happens, verify by content, not by diff, and push the backup branch from HEAD.pnpm lint:all may be red on files you don't own; ship anyway when your own diff is clean under the checks below.node scripts/lint/lines.mjs http://localhost:3005/<page> --theme light
node scripts/lint/lines.mjs http://localhost:3005/<page> --theme dark/, /handbook, the three singularity homes (dossier, orbit, signal) and every page the round touched.pnpm check:pages --preset quick --pages <id>,<id> # the round's touched pages
pnpm check:pages # every page on every device, before a releasescripts/check/pagecheck/ (its README explains the tool) loads every named page on the dev server on the device table in scripts/lib/site-pages.mjs: phones from 320x568 to 430x932 and on their sides, tablets from 768x1024 to 1366x1024 in both orientations, desktops from 1280x720 to the 3440x1440 ultrawide, Kevin's 1527x814 laptop and 1440x900 at 200% zoom. A phone or a tablet is read as a touch device whatever its width. The quick preset reads eight devices in dark and 1440x900 in light; the full preset every device in dark and three in light. Each cell is read once the page's own ready signal has fired and its layout holds still: horizontal overflow, boxes past the viewport edge, text clipped mid-word, console errors and failed resources, the theme applied, the tap targets' hit areas on touch devices, the layout shift score, and the site invariants hooks.mjs names (the 52px toolbar row, the stage rows, the sidebar column, the deck's sheet, the docs contents grid, the gallery's tiles). The declared interactions run in the same queue: the theme flip, the index panel and the preview, the search, the deck's arrow key, every deck slide in its sheet and the deck's grid and book, a docs contents link, the presenter's dock, and the presenter walked slide by slide on every device (each title visible, the "So I built 12" close beat and the grid at 16 prototypes). It writes .pagecheck/REPORT.md with a page by device grid and the run's timing.
.pagecheck/shots/ (ignored by git), with the full page for a failing cell; --sheets adds a contact sheet per device for a quick look at every page.scripts/lint/practices.mjs counts button types, bare effects, any-types, raw hex in TS/TSX ('#xxxxxx'-quoted; unquoted hex inside template CSS snippets doesn't count), and !important. It refuses anything that adds to lint/practices.baseline.json. When files are deleted, prune their baseline entries in the same commit.
scripts/lint/type.mjs holds the type to DESIGN.md section 4 ("Book type"): statically on every pnpm build and pnpm lint:type (family, stack, next/font binding, features, weight, heading and tracking rules, plus a per-file ratchet of literal sizes in lint/type.baseline.json), and against the dev server with pnpm lint:type:live (the face Chrome rendered, the computed features, tracking, optical size and weight). pnpm test:type runs its tests; --update-baseline records a burn-down.
scripts/lint/radius.mjs holds the corners to DESIGN.md section 2 ("Corners: rounded controls, square shells"): statically on every pnpm build and pnpm lint:radius (every radius reads one of the six --pt-radius-<role> tokens, shells and rows stay square, controls are never square, chips read the chip corner, the token values are pinned), and against the dev server with pnpm lint:radius:live (the computed corners at 1440 and 390, the overlays on /brand and the toolbar's hover boxes: square shells, round controls, a picture clipped by its frame). A named exception carries /* lint-radius: allow <reason> */. pnpm test:radius runs its tests.
scripts/lint/heads.mjs holds every book page to DESIGN.md section 4 ("The book page"): statically (pnpm lint:heads, in the build: BookHead's props, page titles from src/lib/page-names.ts, no route restyling the shared head, band or dividers, no second hatch, no guide over a title, the spaces on their tokens), and against the dev server with pnpm lint:heads:live (each head route at 1440 and 390 in both themes: the structure, the title's clearance, the lead, the panel and its Updated day, the mast rule, the band's two rules and the dividers, measured). pnpm test:heads runs its tests.
pnpm exec tsc -p tsconfig.json --noEmitScreenshot every changed visual with the external harness (the in-app browser pane pauses rAF, so shader canvases come out blank):
playwright-core, launched against Chrome for Testing: CHROME_PATH, else the build playwright-core installs (pnpm exec playwright-core install chromium). pnpm capture:pages and pnpm check:pages launch it the same way.localStorage['gt-theme'] = 'dark' in an init script.deviceScaleFactor: 2+ and crop, because full-page shots hide 1px defects.66856750+Kevin-Liu-01@users.noreply.github.com) and end with the session's Co-Authored-By: trailer (decisions log, 2026-10-10).pnpm build:updated --staged after staging the change, and stage src/lib/updated.ts with it. pnpm lint:updated (in the build and pnpm lint:all) fails when a commit touched a book page and the file was not regenerated with it.git push origin HEAD:<branch>), so the work survives a wiped scratch folder or a reset checkout. Main takes only work Kevin approved (section 7).Prototemplate main is the only copy of this code. The public site, www.prototemplate.com, deploys from it through the General Translation team's Vercel project. gt-cloud's apps/redesign stopped being a copy on 2026-09-08, so nothing is copied in from another tree, and no rsync --delete runs toward this repository: it would erase the routes that exist only here.
Kevin decides what lands. Exploration and redesign rounds stay on a branch or on the local dev server until he says to land them (decisions log, 2026-09-14). Approved work lands in this order:
git fetch origin && git log --oneline HEAD..origin/main # other sessions push to main too: take their commits first
git grep -n '^<<<<<<< ' -- src docs deck skills scripts # any hit stops the push
pnpm build > "$TMPDIR/pt-build.log" 2>&1 && git push origin HEAD:main || tail -40 "$TMPDIR/pt-build.log"&&. A pipe such as pnpm build | tail returns tail's exit code and lets a failed build through, which happened on 2026-08-07 and 2026-08-11. A flaky failure with a clean log gets one re-run before diagnosis.git add -A or git add .: the shared checkout holds other sessions' files, including the untracked motion/ folder.git push origin HEAD:main when HEAD is a fast-forward of origin/main. Rewriting shared history needs Kevin.Every command this repository runs, by pnpm <name> (pnpm run doctor, since a bare pnpm doctor is a command of pnpm itself), and the scripts the skills bundle. Skills, documents and the /skills install line name these commands, never a script path, so a script can move without breaking them. Run any script with --help for its full usage.
This file is written by pnpm build:tools from package.json and the opening comment of each file a command runs; pnpm lint:tools fails while it is stale. To change a line, edit that comment, or DESCRIBE in scripts/build/tools.mjs for a command that runs no file of its own.
| Command | What it does | Runs |
|---|---|---|
pnpm build | Runs the key-shape scan and the static lints that guard a deploy, checks src/lib/updated.ts, builds the site with next build, then checks every prerendered head with lint:seo --built. | node scripts/lint/public.mjs --keys && node scripts/lint/... |
pnpm dev | Starts the dev server on port 3005. | next dev --turbopack --port 3005 |
pnpm start | Serves the production build. | next start |
| Command | What it does | Runs |
|---|---|---|
pnpm run doctor | Checks that this machine can run the repository's tools from a fresh clone. | scripts/doctor.mjs |
pnpm postinstall | Runs after every install: compiles the docs site's MDX collection (source.config.ts, content/docs) into .source/ with fumadocs-mdx, which the type check and the docs routes import. | fumadocs-mdx |
| Command | What it does | Runs |
|---|---|---|
pnpm lint:all | Runs lint:static, then the live lints against PT_BASE (default http://localhost:3005). | pnpm lint:static && pnpm lint:lines:shell && pnpm lint:ty... |
pnpm lint:code | Runs oxlint with the gt-ui plugin (scripts/lint/oxlint-plugins/gt-ui.ts) over the live surfaces under src/. | oxlint |
pnpm lint:copies | Holds every copy in this repository to its record in scripts/lint/copies.json. | scripts/lint/copies.mjs |
pnpm lint:heads | Holds every book page to one structure (DESIGN.md section 4, The book page): BookHead renders the front matter (the mast with the title, the lead and the panel, one rule, the note, the contents and the hatch band), every section opens with the shared divider, and every space ... | scripts/lint/heads.mjs |
pnpm lint:heads:live | Holds every book page to one structure (DESIGN.md section 4, The book page): BookHead renders the front matter (the mast with the title, the lead and the panel, one rule, the note, the contents and the hatch band), every section opens with the shared divider, and every space ... | scripts/lint/heads.mjs --live |
pnpm lint:lines | Renders the pages it is given and audits the hairlines they draw: doubled lines from two owners, missing seams between sections and two owners on one seam. | scripts/lint/lines.mjs |
pnpm lint:lines:shell | Audits the shell routes at PT_BASE the same way, and holds every chrome border to its three line tokens. | scripts/lint/lines.mjs --shell |
pnpm lint:pictures | Holds the artifact pictures to the standard (docs/ARTIFACT-PICTURES.md, scripts/media/mood-tone/standard.json). | scripts/lint/pictures.mjs |
pnpm lint:practices | Practice lint with a ratchet: every check counts its violations and records their locations in scripts/lint/practices.baseline.json. | scripts/lint/practices.mjs |
pnpm lint:public | Scans the checkout for what must never be public: key shapes, machine paths and the terms on a private denylist. | scripts/lint/public.mjs |
pnpm lint:radius | Holds the site's corners to one law: rounded controls, square shells (DESIGN.md section 2, Corners). | scripts/lint/radius.mjs |
pnpm lint:radius:live | Holds the site's corners to one law: rounded controls, square shells (DESIGN.md section 2, Corners). | scripts/lint/radius.mjs --live |
pnpm lint:registries | Reports drift between the registries a Prototemplate page, document or skill has to appear in. | scripts/lint/registries.mjs |
pnpm lint:seo | Holds every route's head to the SEO system: each route gets its title, description, canonical, robots, card and JSON-LD from one call to seoRoute() (src/lib/seo/route.ts), under the policy in src/lib/seo/policy.ts. | scripts/lint/seo.mjs |
pnpm lint:seo:live | Holds every route's head to the SEO system: each route gets its title, description, canonical, robots, card and JSON-LD from one call to seoRoute() (src/lib/seo/route.ts), under the policy in src/lib/seo/policy.ts. | scripts/lint/seo.mjs --live |
pnpm lint:shell | Token discipline for the shell layer: no raw color literals, every stroke and fill must draw a token (var(--shell-*), var(--tc-*), or a local custom property defined at a root class). | scripts/lint/shell.mjs |
pnpm lint:skills | Builds src/lib/skills.ts, the typed registry behind /skills, /skills/<slug> and the raw files under /skills/<slug>/, from the curated skills in this repository: skills/<slug>/SKILL.md and the supporting files beside it. | scripts/build/skills.mjs --check |
pnpm lint:static | Runs every lint and test that needs no running server; the gate CI and each lane run. | pnpm lint:shell && pnpm lint:practices && pnpm lint:type ... |
pnpm lint:tools | Builds docs/TOOLS.md, the index /handbook/tools serves: every pnpm command in package.json with the first sentence of the opening comment of the file it runs, grouped by its prefix, then every script a skill bundles (skills/<slug>/scripts/) and the environment the tools read. | scripts/build/tools.mjs --check |
pnpm lint:type | Holds the site's type to one system: the rsms InterVariable through the tokens in src/components/viewer/tokens.css (DESIGN.md section 4, "Book type"; BRAND.md section 6). | scripts/lint/type.mjs |
pnpm lint:type:live | Holds the site's type to one system: the rsms InterVariable through the tokens in src/components/viewer/tokens.css (DESIGN.md section 4, "Book type"; BRAND.md section 6). | scripts/lint/type.mjs --live |
pnpm lint:updated | Builds src/lib/updated.ts: the date each page with a book head last changed, shown in the Updated row of the head's panel (DESIGN.md section 4, The book page). | scripts/build/updated.mjs --check |
| Command | What it does | Runs |
|---|---|---|
pnpm test:copies | Tests for scripts/lint/copies.mjs: node --test scripts/lint/copies.test.mjs (pnpm test:copies). | scripts/lint/copies.test.mjs |
pnpm test:heads | Tests for scripts/lint/heads.mjs: a passing and a failing fixture per static rule, and each live judge fed recorded geometry (the judges are pure functions over what collectHead measures, so no browser runs here). | scripts/lint/heads.test.mjs |
pnpm test:pictures | Tests for scripts/lint/pictures.mjs: the repository passes, and each kind of defect in a fixture copy of the picture files is reported. | scripts/lint/pictures.test.mjs |
pnpm test:public | Tests for scripts/lint/public.mjs: node --test scripts/lint/public.test.mjs (pnpm test:public). | scripts/lint/public.test.mjs |
pnpm test:radius | Tests for scripts/lint/radius.mjs: a passing and a failing string per static rule, the live judges over recorded corners, and the repository. | scripts/lint/radius.test.mjs |
pnpm test:seo | Tests for scripts/lint/seo.mjs: a passing and a failing fixture per static rule (the passing page fixture is the real src/app/page.tsx), the ratchet, the head and sitemap judges on fixture HTML, the image header reader, and one test that the repository passes. | scripts/lint/seo.test.mjs scripts/lint/seo-*.test.mjs |
pnpm test:skill-scripts | Runs the offline test of every script a skill bundles: each skills/<slug>/scripts/<name>.test.mjs with node --test, <name>.test.py with python3 and <name>.test.sh with sh, from the checkout root. | scripts/skills/script-tests.mjs |
pnpm test:skills | Tests for scripts/skills/install.mjs: node --test scripts/skills/install.test.mjs (pnpm test:skills). | scripts/skills/install.test.mjs scripts/skills/script-tes... |
pnpm test:type | Tests for scripts/lint/type.mjs: the repository passes, and each rule reports its defect in a fixture string and passes the fixed string. | scripts/lint/type.test.mjs |
pnpm test:updated | Tests for scripts/build/updated.mjs, each in a throwaway git repository with two pages, so nothing here reads or writes the checkout. | scripts/build/updated.test.mjs |
| Command | What it does | Runs |
|---|---|---|
pnpm capture:pages | Captures the first fold of every page the index panel and the sidebar preview (directive 8.6) against the running dev server, in both themes, into public/shots/pages/<id>-light.jpg and <id>-dark.jpg, where <id> is the surface id in src/lib/surfaces.ts. | scripts/check/capture-pages.mjs |
pnpm check:pages | The page check: one line of automatic reads and a capture of the first screen per (page, device, theme) cell on the running site, the declared interactions with before and after captures, and REPORT.md with the defects, the notes, a page by device grid, the presenter's and the ... | scripts/check/pagecheck/pagecheck.mjs |
| Command | What it does | Runs |
|---|---|---|
pnpm build:deck | Builds public/brand-deck.html, the standalone brand deck viewer, from the deck source checked in at deck/. | scripts/build/deck.mjs |
pnpm build:inter | Split the rsms InterVariable roman and italic into unicode-range subsets, and cut the two static Latin TTF instances the social card route draws with (pnpm build:inter; needs fonttools and brotli). | scripts/build/subset-inter.py |
pnpm build:marks | Builds the speed marks under public/marks: the race-type set for General Translation (wide letters, a forward slant, one horizontal cut through the letters, speed bars into the first letter), every file one color in currentColor with a tight viewBox, so the marks page inlines ... | scripts/build/speed-marks.mjs |
pnpm build:motion | Builds src/lib/motion.ts, the typed data behind /motion and /motion/<slug>, and public/motion/<slug>.md, the research package each translation-series page renders, from the motion folder: the roster in motion/MOTION.md (its "## The films" section, one "### <slug>: <title> ... | scripts/build/motion.mjs |
pnpm build:skills | Builds src/lib/skills.ts, the typed registry behind /skills, /skills/<slug> and the raw files under /skills/<slug>/, from the curated skills in this repository: skills/<slug>/SKILL.md and the supporting files beside it. | scripts/build/skills.mjs |
pnpm build:thumbs | Builds the preview thumbnails the preview layer reads (directive 8.6): public/shots/thumb/<stem>.webp and <stem>-dark.webp at 640x360 (the 320x180 card at 2x), cut from the 1440-wide exhibit captures under public/shots. | scripts/build/thumbs.mjs |
pnpm build:tools | Builds docs/TOOLS.md, the index /handbook/tools serves: every pnpm command in package.json with the first sentence of the opening comment of the file it runs, grouped by its prefix, then every script a skill bundles (skills/<slug>/scripts/) and the environment the tools read. | scripts/build/tools.mjs |
pnpm build:updated | Builds src/lib/updated.ts: the date each page with a book head last changed, shown in the Updated row of the head's panel (DESIGN.md section 4, The book page). | scripts/build/updated.mjs |
pnpm gen:all | Runs every generator in write mode. | scripts/build/gen-all.mjs |
pnpm mood-tone | Cuts the artifact pictures' tone grids to the house standard and writes the manifest the lint reads (docs/ARTIFACT-PICTURES.md). | scripts/media/mood-tone/mood-tone.mjs |
| Command | What it does | Runs |
|---|---|---|
pnpm skills:install | Installs Prototemplate's curated skills (skills/<slug>/, the canonical copy) into any project or agent home, so Claude Code, Codex and other agents load them there. | scripts/skills/install.mjs |
pnpm skills:stale | Counts how often each skill loaded in Claude Code sessions on this machine. | scripts/skills/usage.mjs --stale |
pnpm skills:usage | Counts how often each skill loaded in Claude Code sessions on this machine. | scripts/skills/usage.mjs |
| Command | What it does | Runs |
|---|---|---|
pnpm graphics:audit | Audit the generated visuals in a real browser: every visible text must be at least MIN_TEXT px on the 1600px stage after zoom-to-fit (declared size × fit scale), and content boxes should not overlap. | graphics/build/audit.js |
pnpm graphics:export | Export the rendered visuals for the gt-cloud blog post. | graphics/build/export-blog.py |
pnpm graphics:gen | Writes the illustration pages from graphics/build/manifest.json. | graphics/build/gen-visuals.js |
pnpm graphics:render | Renders the illustrations through agent-browser (docs/GRAPHICS.md). | graphics/build/render.sh |
pnpm graphics:serve | Serves graphics/ on port 8765 for the illustration toolchain (docs/GRAPHICS.md). | graphics/serve/server.js graphics |
Each skill carries the scripts its procedure calls, self-contained so an installed copy runs on its own. Run one from a checkout with node skills/<slug>/scripts/<file>; pnpm test:skill-scripts runs their offline tests.
| Skill | Script | What it does |
|---|---|---|
gt-aesthetic | measure-type.mjs | measure-type.mjs: reads the type of a page's headings and leads the way Kevin reviews it, at each width and theme, and flags what the gt-aesthetic review standard calls a defect. |
gt-deck | check-deck.mjs | Reads the brand deck's source in a Prototemplate checkout and reports where the slide files and the places that restate them disagree. |
gt-deck | heroicon-symbol.mjs | Prints the sprite <symbol> for one or more Heroicons 20 solid glyphs, in the form deck/parts/head.html uses, read from the @heroicons/react package in a Prototemplate checkout. |
gt-diagrams | deck-page.mjs | Writes the brand deck as one HTML file with its fonts inlined, assembled the way deck/shoot-slide.mjs assembles it (parts/head.html, every slide in order, parts/tail.html, deck/fonts/deck-fonts.css in place of <!--FONTS-->), so figure-check.mjs can open one slide in present mode ... |
gt-diagrams | figure-check.mjs | Captures one figure at 2x device pixels in the light and dark themes and reports the SVG facts the line auditor cannot see, because scripts/lint/lines.mjs skips every element inside an svg or a canvas: - labels whose rendered size is under the surface's floor (--min); - labels ... |
gt-explorations | distinct-set.mjs | distinct-set.mjs: checks that a set of options actually differ before Kevin sees them. |
gt-films | measure-render.mjs | Measures a General Translation film render against the motion brief's delivery targets (motion/MOTION.md, "Sound" and "What each film delivers"): H.264 at 1920 x 1080, 60 fps for a final (30 for a draft), AAC audio as long as the video, integrated loudness near -16 LUFS and a ... |
gt-films | scan.mjs | The critic's whole-film scan (skills/gt-films/references/critic.md). |
gt-orchestration | replay-edits.py | Rebuild files from the Write and Edit calls recorded in agent transcripts. |
gt-performance | frame-probe.mjs | frame-probe.mjs: measures how smoothly a page animates, the way the gt-performance skill asks for before and after numbers. |
gt-performance | pixel-diff.mjs | pixel-diff.mjs: proves that a performance change left the picture alone. |
gt-reporting | pr-slate.mjs | Drafts Kevin's PR slate: every open PR he authored in gt-cloud, gt and content, sorted into the groups the slate uses, with the full link, the purpose (the title without its type), the line counts and the facts that put it in its group. |
gt-ship | contact-sheet.py | contact-sheet.py: lays labelled screenshots out on one PNG grid, for a review page or a PR comment that has to show many states at once (gt-ship section 4). |
gt-ship | patch-body.py | patch-body.py: replaces one marked section of a PR body (the screenshots, by default) and leaves every other byte of the body as it was, the bot summaries included (gt-ship section 4). |
gt-ship | pr-assets.sh | pr-assets.sh: uploads PR screenshots to the repository's pr-assets orphan branch under screenshots/pr-<n>/ and prints the link for each file, without touching the checkout's index, working tree or branch (gt-ship section 4, references/pr-body.md "Where the images live"). |
gt-ship | pr-bots.mjs | Reads one PR's review state through the GitHub CLI and reports what still stands between it and a merge: the title policy, the bot summaries and the commit each one reviewed, the unresolved and unanswered review threads, and the checks. |
gt-ship | pr-size.mjs | Groups a branch's diff by kind so every large group in a PR can be justified before it is pushed. |
gt-verify | compare-signatures.py | compare-signatures.py: fetches the same routes from two servers and reports every structural difference between the two renders, page by page (gt-verify references/parity-review.md). |
gt-verify | page-signature.py | page-signature.py: reads the structural signature of one server-rendered page, the unit a parity review compares (gt-verify references/parity-review.md). |
gt-verify | probe.mjs | probe.mjs: measures what renders at one spot of a page, the way a fix is verified before it is reported as done (gt-verify sections 1 and 4). |
gt-verify | row-diff.py | row-diff.py: compares before and after screenshots of the same pages row by row and writes a side-by-side crop of every changed block (gt-verify references/parity-review.md). |
gt-verify | stress.mjs | stress.mjs: walks a scroll story or an animated section through the stress matrix of gt-verify section 3 at several viewports and writes the readings and the captures a reviewer looks at. |
gt-website | routing-matrix.sh | routing-matrix.sh: checks generaltranslation.com's routing cases (real pages, near-miss corrections, section fallbacks, locale prefixes) by the status and redirect target each path answers, so a routing change is gated before it ships (gt-website section 4). |
Each tool reads its setting from a flag first, then the variable, then the default. No tracked file names a machine path.
| Variable | What it sets | Default |
|---|---|---|
PT_BASE | The base URL for the live lints, the page check and the captures; CAPTURE_BASE and REDESIGN_BASE are older names for it. | http://localhost:3005 |
CHROME_PATH | The Chrome for Testing binary the browser tools launch. | playwright-core's chromium.executablePath() |
GT_CLOUD | A gt-cloud checkout, for the tools that compare with it (lint:copies notes). | none |
MOTION_DIR | The motion folder build:motion reads. | <repo>/motion |
PT_DENYLIST | The private term list lint:public reads; skipped under VERCEL or CI. | none |
The index of the componentized instruments. The living version, with the bodies, live plates and API snippets, is "The libraries" in the build log that follows this index on /handbook/libraries (src/app/craft/libraries.ts; /craft redirects there). Keep the two in step: when an engine gains an option, update its entry in libraries.ts in the same round. Once perfected these graduate to their own repos.
| library | entry point | one line | ||||
|---|---|---|---|---|---|---|
| horizon-field | src/lib/horizon-field.ts | WebGL lensing black hole; handle: setParams / pause / resume / renderStatic / destroy. Draws nothing until given a geometry (center + radius). | ||||
| glyph-field | src/lib/glyph-field.ts | Canvas glyph rain, 1,280-glyph typed-array pool, Bayer-dithered far tier, matter-conserving morphs. Options: `drift: 'fall'\ | 'rise', copy: 'auto'\ | 'left'\ | 'top'\ | 'none', glyphScale`. A frame-time governor steps quality down (lean dpr, then the lean pool) when measured cadence proves the device can't keep up — down only, pool cuts at idle. |
| prismatic-field | src/components/shared/PrismaticField.tsx | The chroma wash behind dark bands; presets '1'/'2', exposureScale is the dimmer. | ||||
| dither | src/lib/dither.ts | CPU 1-bit renderer: any fn(u,v,t)→0..1 through the 8×8 Bayer screen; field factories (radialBurst, globe, streakBands, gradientRamp, makeGlyphField) + combinators. | ||||
| studio-field | src/lib/studio-field.ts | GPU Bayer family — the codified BAYER_PRESETS roster (10 variants, default BAYER_DEFAULT_ID = 02 bayer-8x8). Switch by remount; one shared GL context. React wrapper: src/components/shared/StudioField.tsx. | ||||
| iso | src/app/d/toolchain/diagrams/iso.ts | The isometric kit: project/faces/silhouette for boxes, IsoPrism for convex plan polygons, plane() seats flat art, markPath() lays bars. | ||||
| DitheredMark | src/app/d/toolchain/diagrams/DitheredMark.tsx | A masked brand mark + Bayer specular shimmer; shineTravel() gives drivers their tween endpoints. Worn by the tower capstone. | ||||
| DoubledLine | src/components/shared/diagrams/DoubledLine.tsx | The two-thread stroke as a component; optional two-tone via half-plane clip; pulse slot between threads and core. | ||||
| EdgeGlobe | src/app/d/toolchain/diagrams/EdgeGlobe.tsx | The delivery globe — ink-only orthographic sphere, five PoPs, one accent route; pairs with a static Bayer atmosphere (dossier GlobeAtmosphere). | ||||
| LocaleTag | src/app/d/toolchain/components/LocaleTag.tsx | The one locale pill: flag print + mono code; hosts supply the box. | ||||
| RevealSeam | src/app/d/toolchain/sections/RevealSeam.tsx | The slide-to-reveal slider; writes --seam-cut; skins are host CSS (the dossier refit is the house handle). | ||||
| EverySentence | src/components/shared/EverySentence.tsx | The sentence-rewriting glyph reassembler; host-owned clock via ref.setLocale(loc) (calls landing before boot are buffered); hops sets the re-spread beats, 1–5, default 2 — the dossier hero runs 1. | ||||
| TranslateWindow | src/app/d/_v0/TranslateWindow.tsx | The dossier hero's windowed demo, extracted whole so any home can mount it — and the home of the locale belt: an infinite conveyor of LocaleTag chips whose crossing of the strip zone's centre is the page's one clock (render and payload retype from the same dial). Hover pauses it, any interaction holds it, a click slides the picked chip to centre; onLocaleChange vents the active locale to hosts (the dossier headline runs on it). | ||||
| V0FullStack | src/app/d/_v0/sections/FullStack.tsx | The scroll-scrubbed stack story: beats anchor on the copy block's centre taking the 55% read line, the spotlight fires early at the 80% line, and a piecewise clock holds at each lock-in (DESIGN.md §14). The figure is CSS sticky — JS never moves it; the mobile stage runs the svh/dvh law (§13). | ||||
| gallery-shoot | scripts/check/gallery-shoot.mjs | The anatomy wall's tile factory: section-anchored element shots of the flagship (desk/mob × light/dark), theme pre-set via localStorage['gt-theme'], manifest.json written beside the tiles; a missed selector is reported, never fatal. Not a mounted engine — a harness. | ||||
| mobile type ladder | src/app/d/toolchain/styles.css (the late 720px block) | The ≤720px --tcm-* token roster every mobile floor consumes as var(--tcm-X, <px fallback>). Not an engine — a contract; full law in DESIGN.md §12. |
Lifecycle contract for every mounted instrument above: lazy mount behind an IntersectionObserver, self-pause offscreen, one still under prefers-reduced-motion, destroy() tears down everything the instance owns, ink re-resolves on theme flips.
Sixteen directions and three full sites is the visible output. Underneath them is the part I actually spent the time on: a set of laws about lines and color, the tooling that enforces those laws mechanically, and a family of visual engines built as standalone libraries. What follows is the inventory, with the diagrams, the invocations, and the engines themselves running live.
Every direction runs on one typographic rule: structure comes from hairlines, and every line is drawn exactly once. That is easy to say and impossible to maintain by eye, so the repo lints its own pixels. The line auditor loads every page in a real browser (both themes, two widths), reconstructs every rendered line from computed styles (borders, outlines, spread shadows, thin filled boxes, pseudo rails clamped to their clipping ancestors, and the 1px ground reveals framed rows use as seams), then fails the round on four classes of defect:
One command audits a page; a finding names the two owners, the gap between their strokes, and the span over which they run in parallel. The round below is staged (this page audits clean in both themes), but the shape is the auditor’s own:
$ PT_BASE=http://localhost:3005 pnpm lint:lines /docs --theme dark
{
"1440": {
"total": 212,
"doubles": [
{ "orient": "h", "at": 1284, "gap": 1.5,
"a": "pt-sec pt-post-sec", "b": "pt-hatch", "span": 1170 }
],
"missing": [
{ "kind": "section", "between": "pt-sec → pt-foot", "at": 4620 }
],
"selfStacks": [
{ "owner": "pt-compare-tag", "side": "top", "at": 3892, "len": 118 }
],
"invisibles": []
},
"1280": { "total": 208, "doubles": [], "missing": [], "selfStacks": [], "invisibles": [] }
}Deliberate devices (the brand’s doubled threads, marquee rails, terminal frames) live on a small allow list, so the auditor stays strict everywhere else. Two more linters ride along: a shell linter that bans raw color literals in the component layer (every stroke goes through the hairline tokens), and a practices ratchet that counts button types, bare effects, any-types, raw hex in markup and !important in CSS, and refuses any commit that adds to the count.
A dithered field changes state by changing tone on one cell grid; nothing crossfades in alpha, nothing wipes, nothing slides. Both states share the cell size and the phase of the Bayer tile, anchored to one page cell, so the first frame of a transition is the last frame of the state before it, cell for cell. An unanchored tile re-dithers the same tone into different cells, and the eye reads that as a flash even when no tone has moved.
The globe resolves into the Blue Marble on that grid. The globe’s field and the picture’s tone are read at the same cells; one smoothstep over 350 ms mixes the two tones, and the ink is interpolated from the globe’s blue to the picture’s ink on the same curve. The picture’s curve is solved so the mean tone inside its disc equals the globe’s, and brightness holds while the texture changes. Measured on the dashboard build, the two states have identical cells at t0 and the disc’s lit ratio stays within one percent across the resolve.
A step from one picture to the next is the same mix over 150 ms. An interrupted mix restarts from the frame on screen, so a run of quick steps never returns to a picture that has already gone. The system refuses alpha fades, wipes, masks that move, content entrance animation, and any change of cell size inside a transition. Under reduced motion the field shows the end state. Five rules cover all of it:
The plate below runs the loop live on the CPU renderer at 1px cells. The globe turns for two seconds, resolves into the Blue Marble over 350 ms, holds for a second and a half, steps into the Rosetta Stone over 150 ms, holds again, and mixes back into the globe over 350 ms. Both pictures are the tone grids of the deck’s mood slides, 1600 by 900 cells of continuous tone cut to the artifact picture standard, decoded in the browser through a canvas, and their cells print white at 0.62 over the plate’s ground. The plate is 280px tall, so each picture is scaled to cover it; a loop cell that covers one grid cell reads that cell, and a loop cell that covers more reads their area average, so the screen re-dithers the grid’s density at its own cell. The globe is drawn on the disc the Blue Marble occupies. As the resolve begins, the globe’s tone is scaled to the picture’s mean over that disc, so the mix changes texture and ink and holds brightness. Under reduced motion the plate shows the Blue Marble as a still.
The mix is one combinator in the dither module and one clock in the plate. The clock sets the field and the ink together, and the loop draws whatever it holds at its next frame:
/* src/lib/dither.ts: a linear blend of two fields at one amount */
export function mixFields(a: FieldFn, b: FieldFn, amount: number): FieldFn {
return (u, v, t) => {
const av = a(u, v, t);
return av + (b(u, v, t) - av) * amount;
};
}
/* TransitionDemo.tsx: one smoothstep over 350 ms sets the field and the ink together;
the picture ink is the standard's dark screen, white at 0.62 */
const k = smoothstep((now - t0) / RESOLVE_MS);
loop.setField(mixFields(turning, earth, k));
loop.setOptions({ ink: lerpInk(inks.globe, inks.picture, k) });The page’s spine is a ruled column with one rail on each side, drawn once by the column’s own edges and by nothing else. What looks like a simple frame is an ownership system:
The overlap is the named antipattern all of this exists to kill: two owners each drawing a real line along the same edge, compositing into one band darker and thicker than any line the system allows. This page carried one. The diagram above used to run the row’s 1px ground reveal against the inner rail, so the reveal and the rail hairline stacked into a literal doubled border wherever the framed cells met the column. The fix is now drawn into the figure: where a row meets a line that already exists, the cell sits flush and that side’s reveal is dropped. A reveal never runs beside a rail; a border never runs beside a seam.
Two devices keep the law workable at junctions. Between sections, the diagonal-hatch spacer owns the boundary once (one hairline under a 45° hatch band, the strip you see between every part of this page), so two closes never argue over the same edge. And where two hairlines must legitimately cross, a border cross can be added: a small plus seated exactly on the intersection, the way a printer’s registration mark declares a crossing deliberate. The diagram wears two, where the nav’s close meets the rails.
The ownership system is not a convention to remember. It is componentized, so the wrong line is impossible to draw. A rails wrapper renders the page pair; the row renders one hair-colored ground under 1px gaps; cells have no border props at all:
import { BentoCell, BentoRow, Rails } from '@/components/shell/Bento';
<section className='relative'>
{/* the wrapper draws the page rails, once */}
<Rails />
{/* the row owns the seams: gap-px cells over the one hair ground */}
<BentoRow cols='7fr 5fr'>
<BentoCell title='Ship in every language' sub='One pipeline'>
<LocaleLedger />
</BentoCell>
<BentoCell framed={false} cell='is-terminal'>
<Terminal />
</BentoCell>
</BentoRow>
</section>The corner notches on hero cards are not drawn. They are the ground showing through, so a corner can never disagree with the seam that meets it. Sections separate with hatch spacer bands (the diagonal you see between every part of this page) rather than empty margin, and light diagrams sit on exactly one sanctioned second surface, mirroring the ink and raised-ink pair that dark mode runs on. The four-color palette (ink, raised ink, titanium, paper) allows one bright white, one spectral accent per page, and earns depth with lines and material instead of shadows.
The signature visuals are not page code, and neither are the instruments around them. Each entry below is root-agnostic with a real contract (options in and a handle out, or one component with one prop surface), living in its own module. Once they are perfected they will be open-sourced on GitHub as individual libraries. Every plate is the real thing, mounted the way a consumer mounts it: engines are created when the plate first scrolls near and paused by their own observers when it leaves, a single still under reduced motion; the drawings are the actual geometry modules; the seam is the actual slider.
The lensing black hole: a photon ring, wrapped accretion arcs, and the page’s own ruled lines bending into the mass, in one WebGL fragment shader on one quad. Twenty-one tunable parameters (geometry, doppler, chroma, exposure, breathing), a full runtime handle (setParams, pause, resume, renderStatic, destroy), and GLSL kept comment-free by design so the shipped source stays a fraction of the page it lights.
import { createHorizonField } from '@/lib/horizon-field';
const field = createHorizonField(canvas, {
speed: 0.5,
params: { ink: [1, 1, 1], exposure: 2.6 },
});
/* the shader draws nothing until it is given a geometry */
const fit = () => {
field?.setParams({
center: [canvas.clientWidth / 2, canvas.clientHeight / 2],
radius: Math.min(canvas.clientWidth, canvas.clientHeight) * 0.32,
});
};
fit();
new ResizeObserver(fit).observe(canvas);
/* handle: setParams · pause · resume · renderStatic · destroy */A canvas-2D particle field of 1,280 glyphs from eight writing systems that condenses into the word "language" in script after script. One preallocated typed-array pool, a 1-bit Bayer-dithered atlas, and morphs that conserve matter: the outgoing word’s dust is the next word’s material, and nothing ever spawns mid-air or vanishes mid-flight. The dossier home hardened it into a real library: a drift option runs the rain down or up on the same negative-safe wrap math (its closing band rises), per-sprite ink bounds cut the blitted area several-fold, depth alpha is quantized so the loop touches globalAlpha a handful of times per pass instead of per glyph, the field is host-aspect-true (homes live in unit coordinates and re-lay onto the live box every resize, so nothing about it is square), and a frame-time governor watches the real cadence: a wide viewport on weak silicon steps down the same quality ladder the phone cut uses (lean device-pixel cap, then the lean pool), down only, with pool cuts waiting for idle rain.
import { createGlyphField } from '@/lib/glyph-field';
const field = createGlyphField({
canvas,
drift: 'rise', // the library's fall, or a rising field (same wrap math)
copy: 'none', // 'auto' infers the copy fold; 'none' is a standalone plate
displayFamily: getComputedStyle(canvas).fontFamily,
monoFamily: getComputedStyle(canvas).getPropertyValue('--pt-mono'),
onScript: (index) => setActive(index),
});
/* the whole teardown: observers, loop, theme watcher */
field?.destroy();The closing band’s material as its own engine: the glyph-field’s eight-script inventory rising off the ink in the same set 34px columns, with a content clearing measured off the live DOM box so glyphs own only the margins. The band mounts run it gated: parked entirely below the tc narrow cut, the header’s full-width strip kept clear so glyphs own only the sides and the foot, a 1.5x device-pixel ceiling and a 30fps cap for the slow drift, and the frame-time governor underneath. It inherited every one of the hero’s flicker lessons: solid tiers on a tier-batched alpha ramp (dither never rides moving glyphs), integer-snapped device-px blits, hysteresis on the clearing rim. On this plate it plays: glyphs shiver as the pointer nears, and a click blows the nearest one up, its shockwave shoving the column neighbors before the field heals.
import { createInkField } from '@/app/d/glyph-rain/sections/band/inkField';
const field = createInkField({
canvas,
clearEl: contentBox, // the field keeps clear of this box, measured live
clearing: 'none', // or flood the whole canvas (this plate)
interactive: true, // pointer wobble + click bursts; bands never set it
displayFamily: getComputedStyle(canvas).fontFamily,
});
field?.destroy();The spectral light behind every dark terminal and band: a flowing wide-gamut wash with presets, speed and exposure control, masked so the light owns the edges and the content owns the dark center.
import PrismaticField from '@/components/shared/PrismaticField';
<PrismaticField
preset='2'
speed={0.5}
params={{ exposureScale: 4200 }}
className='plate-field'
/>
/* presets: '1' wide burst · '2' arc over a dark core.
exposureScale is the dimmer. Raise it under content. */Any continuous field fn(u, v, t) → 0..1, rendered as pure 1-bit ordered dither: each cell compares the field against the 8×8 Bayer matrix, an exact permutation of 0..63, so a flat field lights exactly k pixels per tile and the ramp has 65 tonally linear levels. It draws one device pixel per cell into a small buffer and lets CSS upscale it pixelated, writes every frame through one reused Uint32 view, and ships field factories (radial bursts, a lit globe, streak bands, ramps, a true text SDF) plus combinators to multiply, max and mix them. The loop caps at 30fps, pauses offscreen and on hidden tabs, and renders exactly one still under reduced motion.
import { createDitherLoop, gradientRamp, streakBands } from '@/lib/dither';
const loop = createDitherLoop(
canvas,
streakBands({ bands: 22, waviness: 0.13, taper: 0.5 }),
{ scale: 3, paper: 'transparent', fps: 30 }
);
/* fields swap live; ink re-resolves on theme flips */
loop.setField(gradientRamp({ angle: Math.PI / 2, smooth: true }));
loop.setOptions({ ink: getComputedStyle(canvas).color });
loop.destroy();The GPU sibling of the CPU renderer above: the house Bayer looks themselves, codified. Ten variants live in one module as fragment shaders on a single session-singleton WebGL context, exported as the BAYER_PRESETS roster: slot 01 is the founder’s first-survey pick untouched, and the rest move along real axes: matrix order (2×2, 4×4, 8×8), cell scale from poster to near-grain, the tone field under the matrix (flow, contours, flank radials, sweeps, interference, breath), motion, and palette balance from ink-dominant to the one white-hot variant. Anything that shows or switches the family maps over that one list, the hero review rig and this plate included. Switching is a remount: programs compile lazily and cache for the session, destroy() keeps the shared context, so cycling never grows the context count.
import StudioField from '@/components/shared/StudioField';
import { BAYER_DEFAULT_ID, BAYER_PRESETS } from '@/lib/studio-field';
const [id, setId] = useState(BAYER_DEFAULT_ID);
const active = BAYER_PRESETS.find((v) => v.id === id);
/* switching is a remount: key the stage, and the outgoing field's
destroy() runs before the incoming one draws. One shared GL
context, one program per preset cached for the session */
<StudioField key={active.id} preset={active.preset} className='plate-field' />
{BAYER_PRESETS.map((v) => (
<button data-on={v.id === id} key={v.id} onClick={() => setId(v.id)}>
<i>{v.id}</i> <span>{v.name}</span>
</button>
))}Every isometric illustration in the family goes through one 30° axonometric map: project(x, y, z) seats the camera at (+,+,+), so exactly three faces of any solid are visible, always lit in the same order from the upper left. A solid is extruded by recipe: an opaque hull from the rounded silhouette occludes whatever sits below, face fills shade it, then the hairlines: rim, top contour, and the interior front edges the silhouette does not already draw. Boxes are no longer the whole kit: IsoPrism extrudes any convex plan polygon with the same visibility and three-tone law, plane(z) is the exported matrix that seats whole flat drawings (glyph strokes, masked brand marks) into any surface, markPath() lays rounded bars in a face, and DitheredMark renders a logo as masked ink with the Bayer-quantized shimmer the tower’s capstone wears. Thickness runs about four percent of footprint, one corner radius serves the whole family, and every drawing spends its accent on exactly one element.
import {
frontEdge, leftFace, rightFace, roundedPolygon,
segment, silhouette, topFace, type IsoBox,
} from '@/app/d/toolchain/diagrams/iso';
/* a raised plate: hull occludes, faces lit top/left/right, then hairlines */
const box: IsoBox = { x: -42, y: -42, z: 0, w: 84, d: 84, h: 3.2 };
const [a, b] = frontEdge(box);
<path className='iso-face-right' d={roundedPolygon(rightFace(box))} />
<path className='iso-face-left' d={roundedPolygon(leftFace(box))} />
<path className='iso-face-top' d={roundedPolygon(topFace(box))} />
<path className='iso-line' d={roundedPolygon(silhouette(box))} />
<path className='iso-line' d={segment(a, b)} />The brand’s connector is one SVG path stroked twice: a full-gauge ink stroke underneath and a narrower surface-colored core on top, carving the ink into two parallel hairline threads at a constant gap along any curve. Because both strokes share one geometry the gap cannot drift on a bend, and non-scaling-stroke holds the gauge in screen pixels even under a stretched viewBox. Now a component: DoubledLine takes the center path, the carving surface, the gauges, and a pulse slot, and it two-tones the pair, one white thread and one gray, by clipping the white copy to a half-plane closed along the same geometry, so the split seam hides inside the carve on every bend (offset clones collapse on curves; concentric restrokes can only make symmetric rings). Draw a later one over an earlier one and the junction re-carves itself into one clean pair: merges cost zero parallel-curve math. This is also the one sanctioned double: one owner, one path, stroked twice; the auditor’s allow list holds it by name.
import DoubledLine from '@/components/shared/diagrams/DoubledLine';
const TRUNK = 'M330 120 L680 120';
/* the split region: the SAME center path, closed off the top edge.
The clip boundary is the line itself, hidden inside the carve */
const SPLIT = TRUNK + ' L680 -20 L330 -20 Z';
<svg viewBox='0 0 720 240' aria-hidden='true'>
<DoubledLine
d={TRUNK}
core='var(--color-ink)' /* the surface that carves */
ink='rgba(255, 255, 255, 0.88)' /* the white thread */
inkB='rgba(255, 255, 255, 0.42)' /* the gray thread */
splitD={SPLIT}
gauge={1}
gap={2}
>
{/* the pulse slot, between threads and core, carved to two
accent hairlines by the same core */}
<path className='pulse' d={PULSE} />
</DoubledLine>
</svg>The translation CDN as an orthographic globe, drawn entirely in ink: front graticule arcs at the family’s regular weight, far arcs as dashed hairlines. Depth is said once, with ink, no fills and no shading. Five points of presence stand on graticule intersections with corner-routed leaders that never cross; one bowed great-circle route from the user to the answering PoP is the drawing’s single accent, and one GSAP loop rides it: request dot out, arrival ring, payload chip back. Behind it the dossier home seats a static Bayer atmosphere: four non-overlapping annuli filled with nested coverage tiers of the ordered matrix on one shared grid, so crossing a ring boundary only ever turns dots off and no cell is painted twice.
import EdgeGlobe from '@/app/d/toolchain/diagrams/EdgeGlobe';
/* the dossier mount: a static Bayer atmosphere hugs the limb. Four
annuli of nested coverage tiers on one shared grid, so crossing a
ring boundary only turns dots off and no cell is painted twice */
<div className='v0-glob-stage'>
<GlobeAtmosphere />
<EdgeGlobe title='Five PoPs, one request served 12 ms away' />
</div>Every bare locale code on every page renders through one component: flag first (a fixed 15×11 print from an SVG flag pack, corners barely eased, never a shadow), then the code in the host surface’s own mono, seated on the text baseline with the flag centered against it. The component carries no box of its own; hosts supply the chip (the hairline pill on light surfaces, the translucent-bordered terminal pill on dark), so the same tag drops into ledgers, capability marquees, diagram key columns and the infinite locale belt. Explicit region subtags fly their own flag (en-GB, ar-EG), bases resolve through a twenty-language map, and an unknown locale degrades to a flagless code rather than a wrong flag.
import LocaleTag from '@/app/d/toolchain/components/LocaleTag';
// bare flag+code chip in a code→output ledger row
<div className='tcb-out-row'>
<span><LocaleTag code='es' /></span>
<b lang='es'>¡Hola, mundo!</b>
</div>
// bordered transcript pill in the hero terminal's ✓-row.
// The host supplies the box, the tag only fills it
<LocaleTag code={loc} className='tc-termloc' />A hundred-line dependency-free slider that drags one CSS custom property, --seam-cut, onto a host box; the top layer clips to it, so the underlayer is revealed in place and content never travels a pixel with the handle. State lives in the CSS var rather than React state: drags cause zero re-renders, GSAP intros and cinema beat scrubs drive the same dial by writing the var directly, and the keyboard path re-reads the live computed value so every writer stays in agreement. The handle is the brand’s doubled line in the dossier refit: two solid threads at the house gap, the run between them filled with the payload’s own ink, bridged by a rectangular grip tab on a 64px hitbox, with a full slider role, arrow-key nudges, and a statically parked cut under reduced motion.
Empieza en minutos.
Publica en cada idioma.
import RevealSeam from '@/app/d/toolchain/sections/RevealSeam';
<div className='tct-app' ref={app} style={{ '--seam-cut': '70%' }}>
<div className='tct-app-main'>{/* rendered UI, full width */}</div>
<div className='tct-payload' aria-hidden>
{/* clip-path: inset(0 0 0 var(--seam-cut)), revealed in place */}
<PayloadJson loc={ploc} />
</div>
<RevealSeam
boxRef={app}
ariaLabel='Reveal the served translation file'
onInteract={() => endTour()}
/>
</div>Now a component: EverySentence owns the dossier hero’s headline engine. One shaped sentence morphs between locales by dissolving into 440 canvas-drawn glyph motes seated on the outgoing text’s own sampled ink, dispersing into a cloud, then reassembling: every glyph flies to exactly one point sampled on a brick lattice and the real text node prints through the settled swarm behind a hard clip front that absorbs each glyph as it passes, mirrored for RTL. It never runs on a timer. The host owns the one clock through a ref handle’s setLocale. Requests debounce a quarter second; one landing mid-dissolve retargets the form boundary, one landing mid-form kills the timeline and re-dissolves, and a same-text locale change retags lang and dir only. Width follows the moving-type law: one shaped probe measure per word, cached, device-pixel snapped, tweened once per cycle. The dossier hero drives it from the locale belt; the plate below drives the same handle from a plain interval.
import EverySentence, {
type EverySentenceHandle,
} from '@/components/shared/EverySentence';
/* the host owns the one clock. The component never runs a timer.
setLocale(loc) is the only intake: debounced a quarter second,
mid-dissolve retargets the form boundary, mid-form kills the
timeline and re-dissolves, same text with a new lang retags only. */
const every = useRef<EverySentenceHandle>(null);
<h1><span>
<EverySentence ref={every} words={WORDS} initial='en' />
</span></h1>
{/* the dossier hero: the locale belt is the clock */}
<TranslateWindow onLocaleChange={(loc) => every.current?.setLocale(loc)} />The translate window’s strip zone is an infinite conveyor of locale chips, and the belt is the demo’s clock, not its decoration: the track carries the fifteen-locale roster twice and its position wraps at one run’s width (the house marquee, ticker-driven so the wrap and the centre logic share one clock), and whichever chip crosses the zone’s centre becomes the active locale, so the rendered page and the payload’s translated leaves retype from the belt’s own output, never a timer’s. Each chip owns the centre for six seconds; hovering pauses the belt, any manual interaction holds it until a quiet spell passes, and a pinned inspector or the Terminal face freezes it outright. Clicking a chip slides the belt the short way round (jumping the position by exactly one run is frame-identical, so the recentre picks the least-travel pairing that stays on the doubled track), with crossings suppressed while it travels, so only the picked locale fires. The window carries one additive vent, onLocaleChange, for a host that wants to run on the belt’s clock (the dossier hero’s morphing headline does); passed nothing, nothing changes. Reduced motion parks the belt with es centred, and clicks recentre instantly.
import TranslateWindow from '@/app/d/_v0/TranslateWindow';
/* the belt is the window's clock: the track carries the roster twice,
wraps at one run's width, and whichever chip crosses the strip
zone's centre fires the rewrite, never a timer */
<TranslateWindow onLocaleChange={(loc) => every.current?.setLocale(loc)} />
/* pacing: BELT_DWELL seconds a chip; any interaction holds the belt
(BELT_IDLE of quiet before it resumes); a pinned inspector or the
Terminal face freezes it outright */The nameplate as a working type-specimen sheet: prototype in serif off the destination line’s upper left, template in grotesk off its lower right, the destination between them as empty outlined text, and every word held by a crop frame, four border-touching rules that move with their word. The take is physical: type falls out and the serif frame’s right rule slides in to re-hug the shorter word; both words then travel inward, frames riding along, until they park inside the outline, and the parked words ARE the nameplate, lowercase, so nothing ever crossfades. Every travel delta is measured from live rects, so the module never encodes where the sources sit; fonts are awaited before anything is measured, a resize rebuilds the sheet, and reduced motion holds the settled nameplate rather than replaying the take.
import PrototemplateHero from '@/app/PrototemplateHero';
/* the take is physical: 'type' falls out, the serif frame's right
rule re-hugs the shorter word, both words travel to the outline
and PARK. The parked words ARE the nameplate, lowercase, so
nothing ever crossfades */
<PrototemplateHero />
/* travel deltas are measured from live rects. The module never
encodes where the sources sit; fonts are awaited before measuring,
resize rebuilds, reduced motion holds the settled sheet */The stack story’s scroll engine, and the two laws that keep a pinned read honest. One scrubbed dial spans the whole read, and a piecewise time map (measured lock-ins in, beat end-times out) converts scroll into story time, so every layer seats exactly as its beat’s copy reaches the read line whatever the beats’ real heights are. A lock-in is the copy block’s centre taking the 55% read line, measured from flow geometry (the finale is sticky, so its own rect would report the stuck pose), and the whole map re-anchors on every refresh. Between scroll and paint sits the stage’s damper: the trigger only moves a target, and one gsap.ticker lerp chases it, calling apply() once per frame. Taps, cap wires, typing and rail all ride one damped clock, the stage’s equivalent of scrub: 0.35. The ticker detaches when settled, parking dead on target so no residue leaks into the beat math, and re-attaches on new input, so a resting stage burns nothing.
/* one dial spans the read; a piecewise map (measured lock-ins in,
beat end-times out) converts it to story time, re-anchored on
every refresh, lock-ins read from FLOW geometry */
const READ_LINE = 0.55; // a beat locks as its copy centre takes it
/* the scrub's damper: the trigger only moves a TARGET; one ticker
lerp chases it and calls apply() once per frame */
const retarget = (p: number) => {
pTarget = p;
if (!scrubOn && Math.abs(pTarget - pCur) >= SCRUB_EPS) {
scrubOn = true;
gsap.ticker.add(scrubTick); // detaches itself when settled
}
};The landing wall’s compare instrument: two faces of one site (the home and the enterprise page) overlaid with the house seam between them, the reveal-seam recipe refit for a wall of stills. The cut lives in one CSS custom property on the stage, so a drag re-renders nothing; pointer capture on the stage keeps the sweep when the pointer leaves the box mid-drag; and a hidden range control drives the same var, so keyboards and readers get a real slider rather than a pantomime of one. Every face image carries draggable={false}, since one native image-drag would steal the pointer stream and freeze the seam mid-sweep, and each face is a light/dark pair, so the rig follows the page theme without a re-shoot.
import SiteCompare from '@/app/SiteCompare';
/* the cut is one CSS var on the stage. Dragging re-renders nothing */
<SiteCompare slug={site.slug} name={site.name} />
/* inside: pointer capture keeps the sweep when the pointer leaves
the box; a hidden range control drives the same var for keyboards;
draggable={false} on every face, since one native image-drag would
steal the stream and freeze the seam mid-sweep */The harness that stocks the landing wall: a real Chromium at the dev server, shooting the flagship home’s sections, both themes at both cuts. Element screenshots, never scroll depths: each tile is one section’s own box, so side-by-side pairs align whatever the viewport was. Theme is seeded through the same pre-boot door the site itself uses (an init script writes gt-theme to localStorage before navigation, the root script stamps it before first paint), one full scroll pass settles every lazy-armed section before the first shot, and a selector that misses is reported and skipped, never fatal. The run’s last act writes a manifest of the tiles beside them. The wall renders what was actually shot, so a missing tile is a skipped cell rather than a broken image.
$ node scripts/check/gallery-shoot.mjs public/shots/gallery
/* element screenshots, never scroll depths: a tile is one section's
own box, so side-by-side pairs align at any viewport */
const file = `sec-${sec.key}-${cut.key}-${theme}.jpg`;
await page.locator(sec.sel).first().screenshot({ path: file });
/* dark cuts ride the pre-boot door: theme seeded before first paint */
await context.addInitScript((t) => localStorage.setItem('gt-theme', t), theme);
/* the run ends by writing the manifest the gallery imports. A
missed selector is a reported skip, never a broken tile */Not an engine: the system that makes a whole page’s mobile typography one decision. Under 720px the page root publishes a ladder of slots: heading sizes with their own line heights, lead, body, small and kicker steps, head and cell paddings, box air, and a 20px seam floor, the least air card copy may keep off a ground strip. Every floor below consumes the slots instead of restating sizes, so a compact-cut change is one token edit rather than a hunt across sections. The block’s position is itself a law: it is the last 720px block in the sheet, because several base rules it overrides appear after the earlier one, and these same-specificity declarations only win by following them. The ladder outranks by order, never by escalating selectors.
/* styles.css: the LAST 720px block; every floor consumes the slots */
@media (max-width: 720px) {
.toolchain-root {
--tcm-h2: 2.25rem; --tcm-h2-lh: 1.18;
--tcm-lead: 17px; --tcm-body: 16px;
--tcm-head-pt: 72px; --tcm-box-gap: 28px;
--tc-card-pad: 20px; /* the seam floor */
}
/* same specificity as the base rules. The ladder wins by ORDER,
so it lives after every rule it must override */
.toolchain-root .tc-head h2 {
font-size: var(--tcm-h2, 36px);
line-height: var(--tcm-h2-lh, 1.18);
}
}Two inline scripts in the document head run before the app exists, and everything that films or embeds the site leans on them. The first stamps the persisted theme onto the root element before first paint. Dark mode is a token remap, so one attribute set early is the difference between no flash and a white flash on every dark load; the screenshot harness enters by the same door, seeding localStorage before navigation. The second is the rAF gate: requestAnimationFrame is wrapped so an embedding parent can freeze and resume every animation loop on the page with one postMessage. Queued callbacks flush on resume, so shaders and scroll loops pick up exactly where they stopped rather than rebooting. That gate is how the presenter idles a whole wall of live thumbnails without touching any engine’s own code.
{/* layout.tsx: two scripts that run before the app exists */}
{/* the persisted theme, stamped before first paint: no flash */}
<script dangerouslySetInnerHTML={{ __html:
"try{var t=localStorage.getItem('gt-theme');" +
"if(t)document.documentElement.dataset.theme=t}catch(e){}" }} />
{/* the rAF gate: postMessage({type:'gt:freeze',frozen}) pauses every
loop on the page; queued callbacks flush on resume */}Not an engine: the ground the engines stand on. Four absolute colors: ink, raised ink, titanium, paper. Structural color everywhere derives from these (every text step is ink or white at some alpha, every hairline is titanium at some alpha, every hatch is a thin ink or white veil), and each page adds exactly one spectral accent on top. Pages never touch the raw values: each root class publishes a semantic layer (surfaces, ink steps, hairlines, hatch), and dark mode is a pure custom-property remap in which the paper family collapses onto ink, ink flips to white, and hairline alphas rise so a 1px seam still survives between two ink surfaces.
/* globals.css: the four colors, absolute by design */
@theme {
--color-ink: #070707;
--color-ink-raised: #101010;
--color-titanium: #8a8f98;
--color-paper: #ffffff;
}
/* each root publishes a semantic layer of the four (+ alpha) … */
.toolchain-root { --tc-paper: #ffffff; --tc-ink: #070707;
--tc-hair: rgba(138, 143, 152, 0.26); --tc-accent: #2f5ce0; }
/* … and dark mode is a token remap, nothing else */
[data-theme='dark'] .toolchain-root { --tc-paper: #070707;
--tc-ink: #ffffff; --tc-hair: rgba(138, 143, 152, 0.5); }Every animation obeys the same discipline as the lines. The morphing headline is a single shaped text node (never per-character spans, which would break Arabic joining and Devanagari matras), with its width measured from a hidden probe and tweened once per cycle, device-pixel snapped. The locale belt seats each glyph on the orbit’s tangent and rolls words over at the sides so text never inverts, with the flag guiding each rewrite. And the compare seams on the index are the same slide-to-reveal instrument the toolchain hero uses to pull its rendered app back to the payload underneath.
How the illustrations for the General Translation blog are made, from the brief to the carousel, with the toolchain that lives in graphics/. It produced the thirty-six visuals of "Designing docs for humans" and is the procedure every post after it runs. The gt-graphics skill (skills/gt-graphics, /skills/gt-graphics) carries the same rules into agent sessions; this document is the long form.
One HTML page, 1600 by 900, rendered headlessly at 7680 wide and downsampled to 3840. The page holds real screenshots of the product cut into padded crops, labels and leader lines drawn once, and a glyphfield export as the ground. Nothing in it is invented: every rectangle is a measured region of a captured page, every label names something the reader can find in the product.
The rules the set settled on, each learned from a review:
The article column is about 700 CSS px wide, so a 1600px stage is shown at 0.44x. Everything is sized for that, not for a full-screen viewer:
| element | size on the stage |
|---|---|
| any text | 26px or more after zoom-to-fit; audit.js fails the run below that |
| label chip | 28px Inter 600, 46px tall; tags and badges 30px, 54px and 50px tall |
| numbered dot | 56px disc, 28px numeral |
| panel header | 32px, icon 34px |
| measurement label | 28px Geist Mono on a dark backing pill |
| lines and rulers | 3px; ruler caps 18 by 3px |
| crops | 1.2x or larger; a sparse composition is zoomed to fill 90% of the frame |
| background | drawn at 2x the stage height with its own aspect and square pixels (image-rendering: pixelated), so a 1920 export shows as crisp 2px dots and a card is never stretched; dimmed to 36% under article visuals, full strength under covers |
| export | 3840 wide webp, quality 95; covers 3840 webp in a dark and a light version; OG 2400 by 1260 PNG; clips as 1400 wide GIFs |
| delivery | served through next/image at the column's device width (a dense deviceSizes ladder plus an accurate sizes), quality 95 |
Resolution was never the blur. At 1x the dither's one-pixel dots landed on less than a device pixel and aliased into noise; at 2x with smooth scaling they read as a halftone. Small labels were the other half of it.
The floor is measured after zoom-to-fit, not as declared. A composition wider than 90% of the frame is scaled down, and its 28px labels with it, so when the audit reports text under the floor the usual fix is to make the composition narrower (smaller crops, shorter labels, one label column) rather than to enlarge the type. MIN_TEXT in gen-lib.js holds the floor.
| path | what it is |
|---|---|
graphics/build/gen-lib.js | the primitives and the asset maps: SHOTS (captures with their device pixel ratio), BG (backgrounds), crop, hl, tag, dot, line, elbow, badge, panel, and stage, which wraps a visual with the fonts, the ground and the centering script |
graphics/build/gen-visuals.js | the visuals, one add() each, the page rectangles (N new docs, O old docs), ASSIGN (which export each visual sits on) and BG_WASH |
graphics/build/render.sh | shoots every visual, or the ids given, through agent-browser at 7680 and downsamples with Pillow |
graphics/build/export-blog.py | writes the webp set and the GIF clips into public/static/blogs or --dest |
graphics/build/composite-videos.sh | lays a recording over a still, reading the crop rectangle from the rendered page |
graphics/build/capture-sidebar.sh | the stop-motion capture of the docs sidebar |
graphics/build/rewriting-covers.py | both theme covers of the earlier post: keeps the published cover's ground and framing (graphics/shots/rewriting-cover-ground.png) and lays the light and dark captures into its page frame at one scale, so a theme switch moves nothing |
graphics/build/sheet.py | contact sheets for review |
graphics/build/manifest.json | the generated index of visuals: id, area, name, background, why, size, animated; /graphics reads it |
graphics/serve/server.js | the static server on 127.0.0.1:8765; also accepts POSTs into inbox/ for the DOM-capture trick |
graphics/glyph/ | the glyphfield studio scripts and Kevin's project source |
public/graphics/bg/ | the glyphfield exports, lossless webp identical to the PNG originals: user/ the palette, blue/ the blue duotone and light variants; graphics/bg links here so the toolchain and the site read one set |
graphics/shots/hi/ | the product captures at 3 to 5x, lossless webp |
graphics/fonts/ | Inter and Geist Mono variable |
graphics/rec/ | the recordings the two clips composite from |
Generated folders (graphics/build/visuals/, graphics/out/, graphics/rec/stopmo*/) are ignored by git; everything in them comes back from the scripts.
pnpm install at the root (heroicons for the label icons), pip install pillow, and agent-browser and ffmpeg on the PATH.pnpm graphics:serve keeps graphics/ on port 8765.graphics/shots/hi/ (see "Capturing") and register them in SHOTS with their density.gen-visuals.js.add(id, area, name, bg, why, () => html). Assign it its own export in ASSIGN.pnpm graphics:gen writes the HTML and the manifest; pnpm graphics:audit [ids] measures every text against the floor and lists overlapping boxes (fix the failures, judge the warnings); pnpm graphics:render [ids] shoots them; python3 graphics/build/sheet.py [ids] tiles them for review.graphics/build/composite-videos.sh.pnpm graphics:export writes the webp set and GIFs into public/static/blogs; --dest points at a landing checkout. Bump the ?v= stamp on every reference in the post./graphics and the post on /blog.next/image with the column's sizes and the dense images.deviceSizes ladder in next.config.ts, at quality 95; clips are cut at 1400px, the column's 2x width.Sources come from a landing dev server built from main, captured with agent-browser at a device pixel ratio of 4 or 5 at 1440 by 900, the Next.js dev portal hidden, the pointer parked on plain text. Themes come from agent-browser set media light|dark; the docs follow the preference when no theme is stored. Menus open with their real control. The old docs lived on an SSO-protected Vercel preview; those were captured by serializing the DOM from a signed-in Chrome to the local server and screenshotting the saved page. Convert captures to lossless webp; the library reads webp sizes.
The exports are Kevin's glyphfield project, rendered headlessly through window.glyphfield.studio (graphics/glyph/gfexport.sh). The generator draws one export per visual at exactly 2x the stage and dims it under article visuals; covers keep the full export. Blue duotone variants carry the covers. Every cover has a light-theme twin (light: true on coverExploded and coverWiremap): the same ground and scale, the docs captured in their light theme, the wireframe plate in paper and ink. The post lists the light cover under imagesLight and the blog shows the one for the reader's theme.
The recorder keeps one or two frames per second at 4x, so interactions are captured as stop-motion: pause the Web Animations, step currentTime between screenshots, assemble with ffmpeg at variable frame durations. Screenshots re-fire trusted pointer events on the last clicked element, so the capture blocks real pointer events in the region and drives hovers synthetically. The composite reads the crop rectangle from the rendered visual, overlays the clip, and writes a 30fps MP4 master and the GIF the post embeds: 1600 wide, 256 colours, sierra dither, rectangle diffing.
The post embeds the set with the Carousel and HitList MDX components of the landing blog (children with string attributes; the blog's MDX renderer strips expression props). Covers ship as 3840 webp and the OG image as a 2400 by 1260 PNG; the landing app serves webp covers at quality 95, because dithered artwork smears at the default 75. Posts live in the generaltranslation/content submodule, so a change is two pull requests. When the post needs anything from gt-cloud (a new MDX component, images under public/static/blogs/, redirects or navigation entries), the gt-cloud pull request merges first and the content pull request second; in the other order the post publishes with missing images or a failed build. The content repository's preview app needs a simple version of every component or its build fails. gt-website (references/blog.md, read against gt-cloud main on 2026-10-05) holds both rules. This repository keeps the authoritative copy of the three docs-redesign posts under content/blog and their graphics under public/static/blogs, read by /blog and /graphics.
<rect> plus a path (copy, sidebar). The inliner used to strip width/height from every element, so the rect collapsed and only a corner path drew. It now resizes the root tag only.render.sh shot Chrome's error page and the export shipped it. The render now skips a page that never centres.| symptom | cause | fix |
|---|---|---|
| "pixelated" cover | 1x dither aliasing, plus the optimizer re-encoding at quality 75 | 2x smooth background; webp cover at quality 95 |
| "blurry" diagrams | 12px labels at 0.44x | labels 26px, lines 3px, zoom-to-fit |
| lines clipped | 1px rules and 1.5px caps under a device pixel | 3px everywhere, labels on backing pills |
| hover pill jumped after every click in the clip | screenshots re-fired trusted pointer events | block real pointer events; synthetic hovers |
carousel prop items undefined | next-mdx-remote strips expression props | child elements with string attributes |
| content preview build failed | unknown component | stub every component in apps/content |
| PR policy failed | feat title without a Linear issue | docs(blog) title, or link the issue |
An artifact picture is a photograph or scan of an object, artwork or place, screened through the house dither. In this repository they appear in three places:
| Surface | Pictures |
|---|---|
The plate port's field (src/components/plate) | earth, rosetta, calligraphy, tablet, gloss |
The brand deck's mood slides (deck/) | earth, rosetta, tablet, calligraphy, lighthouse, devanagari, gloss, cable, wave, compass |
The transition demo on /handbook/libraries (src/app/craft) | earth and rosetta, the deck's grids |
On the plate port, the onboarding steps show earth, rosetta, calligraphy and tablet, the OAuth consent states show rosetta, the device flow shows calligraphy, and the CLI wizard shows gloss (gallery/devStates.ts), as on the dashboard.
Procedural fields (the sign-in globe, the ramp), marks, glyph fields, the deck's shader openers and blog covers are not artifact pictures, and these rules do not apply to them.
The same standard covers gt-cloud's dashboard sign-in and onboarding field (.agents/skills/artifact-pictures there). standard.json and mood_tone.py are the same files in both repositories.
| File | Role |
|---|---|
scripts/media/mood-tone/standard.json | The standard: screen, tone, file sizes, windows, retired names |
scripts/media/mood-tone/mood_tone.py | The cutter (Python 3 with Pillow) |
scripts/media/mood-tone/mood-tone.mjs | The recipes for the deck and plate sets, and the wrapper |
deck/shots/tone/mood-{name}.jpg | The deck's tone grids |
deck/shots/tone/manifest.json | One entry per deck grid, written by the wrapper |
public/brand/mood/mood-{name}.jpg | The plate's tone grids, the same bytes as gt-cloud's |
public/brand/mood/manifest.json | One entry per plate grid, written by the wrapper |
public/craft/mood-{earth,rosetta}.jpg | Byte copies of the deck's earth and rosetta grids for the transition demo on /handbook/libraries |
src/app/craft/CraftArticle.tsx | The text around the transition demo, with the demo's credit line |
src/components/plate/brand/moodPictures.ts | The plate registry: src, placement and caption per picture |
deck/slides/NN-mood-{name}.html | The deck's mood slides, one canvas per grid, with the credit |
deck/parts/tail.html | The deck engine that screens the grids |
scripts/lint/pictures.mjs | The lint |
A picture may not be a page of readable English: dictionary entries, book pages, documents, signs or captions. That text reads as copy and distracts from the form beside it.
Writing is welcome when it is the artistic or design subject of the object: carved inscriptions (the Rosetta Stone), calligraphy, manuscripts in other scripts or old hands (Devanagari, the gloss), signs pressed in clay (the tablet), and the lettering engraved on a historical chart or instrument (the cable chart, the compass rose).
Each recipe declares writing: 'none' | 'artistic'. The cutter's text-line detector counts rows of six or more letter-sized marks and records the count as textLines. A picture with any text lines must declare 'artistic'. The detector cannot read meaning, so a reviewer checks every 'artistic' declaration against this rule. Plain English prose never qualifies.
k/language-map, 2026-10-03).moodPictures.ts, which the plate port's field shows, and in the credit line under the transition demo on /handbook/libraries (CraftArticle.tsx). The demo's line carries the deck slide's credit for each picture the demo shows, without the label ("Image:", "Photograph:"). The lint checks the deck slides and the demo's line.scripts/media/mood-tone/README.md lists each file's name, origin, pixel size and sha256, and the wrapper refuses a source whose sha256 differs from SOURCES in mood-tone.mjs.The Blue Marble on the onboarding field is the reference. Its numbers are in standard.json under tone.
Fixed for every picture:
Chosen per picture, in the recipe:
red (the earth's) or gray;invert so the subject is lit on a black ground;scene or marks.Solved per picture by the cutter, never set by hand: the black and white points.
The solver returns levels 80 and 230 for the plate's earth, which is the earth's own recipe.
Sizes: a cover is 1600 by 900, one grid cell per CSS px at a 1440 by 900 view. A disc is 2400 by 1350. The cap is 560 KB (573440 bytes).
standard.json under screen:
(m + 0.5) / 64;Where each surface states these:
| Surface | Cell | Tone floor | Bayer and threshold | Ink and opacity |
|---|---|---|---|---|
| Plate port | PICTURE_SCALE in FieldStack.tsx | TONE_FLOOR in lib/picture-field.ts | BAYER_8 in lib/dither.ts | --tc-picture-ink, --field-picture-opacity in plate.css |
| Deck | MOOD_CELL_PX in deck/parts/tail.html | MOOD_TONE_FLOOR in tail.html | bayer8 in tail.html | --mood-ink, --mood-opacity in deck/parts/head.html |
| /handbook/libraries demo | CELL in TransitionDemo.tsx | TONE_FLOOR in TransitionDemo.tsx | BAYER_8 in src/lib/dither.ts | --ptc-picture-ink, --ptc-picture-opacity in craft.css, the dark values, because the plate is dark in both themes |
The deck engine sizes each mood canvas's backing store to its box on screen (getBoundingClientRect, because the stage is CSS-scaled), so a cell is 1 CSS px on the sheet, in a thumbnail, in the book view and on the full-stage backdrop. It reads the grid at cover fit, centred, and draws again on slide activation, resize and a theme flip.
The cutter measures each grid over the region shown (the deck's full 1600 by 900 sheet; on the plate, the part right of the ramp at 1440 by 900; only the disc or the masked subject where there is one) and records the stats in the manifest. The lint holds them to standard.json under windows:
| Kind | Mean | Std | At white (250 and up) | At the floor (10 and under) |
|---|---|---|---|---|
| scene | 0.30 to 0.42 | 0.28 to 0.44 | at least 2% | at least 5% |
| marks | at most 0.35 | any | at least 0.5% | at least 50% (the ground) |
From the repository root:
pnpm mood-tone <sources dir> [--set deck|plate] [--preview <dir>] [--check]The wrapper sends the recipes and the standard to mood_tone.py, which writes mood-{name}.jpg for each recipe (the deck set to deck/shots/tone, the plate set to public/brand/mood) and prints one manifest entry per picture. The wrapper writes those entries to manifest.json and prints each picture's levels and stats. With --preview, the cutter also saves the 1 px screen of each grid at the view size as preview-{name}.png.
The cutter is deterministic: a run on the same sources writes the same bytes. A grid's sha256 ties the file to its manifest entry, and the lint checks that. It does not show that the entry came from the recipe: an entry edited by hand next to an untouched grid passes the lint. Only --check, run with the sources, proves that. It cuts into a temporary folder and fails unless every grid and the manifest equal the committed files byte for byte. The lint cannot run it, because the sources are not committed. Never edit a grid or a manifest by hand.
A recipe holds only: name, source, the size and cap from standard.json, crop, channel, invert, kind, writing and placement. It holds no tone numbers. The crop kinds are documented above DECK in mood-tone.mjs.
SOURCES in mood-tone.mjs, and add its row to the source table in scripts/media/mood-tone/README.md.DECK or PLATE in mood-tone.mjs. Spread COVER for a cover. Set kind: 'marks' for writing or engraving on a plain ground and kind: 'scene' for anything else. Declare writing. A deck recipe's placement is a cover at focus 0.5, 0.5.--preview and look at preview-{name}.png.deck/slides/NN-mood-{name}.html from an existing mood slide, with <canvas class="mood-img" data-tone="shots/tone/mood-{name}.jpg" role="img" aria-label="...">, the plate's title, sentence and credit, then update SLIDE_COUNT in scripts/build/deck.mjs, SECTIONS in deck/parts/tail.html, DECK_SLIDES in src/lib/search-index.ts, and the entry in deck/shots/OPENERS.md. Run pnpm build:deck. - On the plate port: add the name to PictureName and an entry to MOOD_PICTURES in moodPictures.ts: src is /brand/mood/mood-{name}.jpg, placement equals the recipe's (a disc also carries the manifest's disc values cx, cy and r), and the caption has a title, a note of at most 90 characters that says what the object is, and the credit. Make the same change in gt-cloud.node scripts/lint/pictures.mjs (it also runs before next build and in pnpm lint:all).manifest.json, the recipe, the README row and the slide or registry entry. If the manifest shows textLines above 0, say so in the commit so the reviewer checks rule 1.To remove a picture, delete its recipe, its grid and its slide or registry entry, run the wrapper so the manifest drops it, and move its pages to another picture.
scripts/lint/pictures.mjs runs before next build (pnpm build), in pnpm lint:all, and on its own with pnpm lint:pictures. Its tests are in scripts/lint/pictures.test.mjs. pnpm lint:all runs them after the lint, and pnpm test:pictures runs them alone. It fails when:
src or placement differ from its manifest;mood-* file sits in deck/shots, or public/brand-deck.html names grid files under public/deck-assets other than the manifest's (run pnpm build:deck);<div class="credit">, or the text around the transition demo on /handbook/libraries does not carry the deck credit of each picture the demo shows;writing is not 'none' or 'artistic';textLines is above 0 and writing is not 'artistic';mood-{name} token under deck/, src/ or the built deck, or a string literal in the picture code;The lint checks the screen constants by declaration. It strips comments first (Python # comments and docstrings, HTML <!-- -->, block comments and // comments), so a value quoted in a comment does not count. Each constant must be declared once and never assigned again. Each ink and opacity token may be set only in its own rules, and every declaration there must carry the standard's value: .brand-field-stack (light) and :root[data-theme='dark'] .brand-field-stack (dark) in plate.css, :root (light) and :root[data-theme="dark"] (dark) in the deck's sources and the built deck, and .ptc-plate.is-transition (dark) in craft.css. The lint does not follow how the code uses a constant: a draw call that divides by 2, or a loop that skips the floor, passes. The backstop is to look. Run the wrapper with --preview and compare the page with preview-{name}.png, the 1 px screen of the grid.
standard.json is the same file in gt-cloud and Prototemplate. A change to it is made in both repositories in the same round, and every grid in both is cut again with the new standard.
These names never return as picture names, files or registry keys. They are listed in standard.json under writing.retired, and the lint rejects them.
| Name | What it was | Why it was retired |
|---|---|---|
dictionary | A page of the Oxford English Dictionary | Plain English prose distracts as copy |
johnson | A page of Johnson's Dictionary | Plain English prose distracts as copy |
oed-volumes | The Oxford English Dictionary volumes | The spines carry plain English titles |
oxford | Any page or volume of the Oxford English Dictionary | Its pages are plain English prose |
oed | The Oxford English Dictionary by its initials | Its pages are plain English prose |
Retiring the first two deleted the deck's Design system and Documentation mood slides on 2026-10-05; the deck went from 95 to 93 slides.