Every Versable repo README follows one structure, so that a reader who has seen
one can skim any other and know where things are. The structure is extracted
from enhancement-product, which arrived at it first and is the reference
implementation.
Audience: an agent (or person) writing or fixing a repo README.
The rule this exists to prevent is dull and real: three repos independently grew three different words for the same section, three title conventions, and three running orders, and nobody noticed until someone put them side by side.
The order, and why it is that order#
A README is read by two people. Someone deciding whether this repo is the thing they want, who reads the top and leaves, and someone who has decided and needs to run it, who scrolls. So the order runs from identity to orientation to action to reference, and never mixes them.
- Title. A literal
#heading naming the repo. Above the banner, not replaced by it, because an image is not a title to a screen reader, a search result, or a plaintext view. - Banner. A
hero.svgbeside the README, in every case. Owner ruling 2026-08-13: this applies to directory indexes too, not only to packages and repo roots. The first pass treated indexes as exempt and the result was uneven for no principled reason, which is the thing this doc exists to stop. - Subtitle. One
<strong>line saying what this is, in the reader's terms rather than the system's. Optionally a second plain line elaborating. - Badges. Stack first, then infrastructure. Facts, not decoration.
- Links row. Production, staging, docs. Wherever this thing actually lives.
- What is this? An intro table. This is the section that does the most work and the one most often skipped.
- Architecture. One ASCII diagram, once.
- Quick Start. Clone, then per-part setup, with ports named in the heading.
- The repo's own middle. Deployment, CI, environment, whatever this repo genuinely has. Content differs here; only its placement is fixed.
- Documentation. The docs map.
- Repo Structure. A tree, last.
Sections are separated by ---. Steps 1 through 5 sit inside one
<div align="center">.
The head block#
# Repo Name<div align="center"> <img src="/docs/raw/docs/app-patterns/docs/hero.svg" alt="Repo Name" width="640" /> <br /><br /> <strong>One line on what this is for, in a human's terms</strong> <br /> A second line, optional, on how it does that. <br /><br /> <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white" /> <br /> <img alt="Cloud Run" src="https://img.shields.io/badge/Cloud%20Run-4285F4?logo=googlecloud&logoColor=white" /> <br /><br /> <a href="..."><strong>Production</strong></a> · <a href="..."><strong>Docs</strong></a></div>Authoring the hero#
Hand-authored SVG, no image generation, which costs minutes per image and buys
nothing at this size. The house shape is a mark on the left, a monospace wordmark,
a one-line subtitle, and a row of outlined stat pills. Indigo-to-cyan gradient
(#6366f1 to #0ea5e9) for the mark, #8b93a7 for secondary text, so it holds
on both GitHub themes. Do not use prefers-color-scheme inside the SVG; GitHub
strips embedded style blocks.
Compute pill widths, do not eyeball them. Every hero drafted by hand in this
project shipped a first version where a pill's text overran its border, caught
only by looking at the raster. At 11.5px monospace, width = len(text) * 7.05 + 26
with the text inset 13px holds.
Render-check on both backgrounds before committing. Chrome headless against a wrapper HTML is enough:
chrome --headless --screenshot=out.png --window-size=660,220 file://wrapper.htmlThen look at the image. A green command is not the check; the check is your eyes on the pixels.
Badges are img.shields.io/badge/<label>-<version>-<hex>?logo=<slug>&logoColor=white.
Put the language and framework row first, the infrastructure row second. A badge
carries a fact a reader would otherwise have to dig for, usually a version. It is
not a trophy shelf: if you cannot say what question a badge answers, drop it.
The intro table#
The one section people skip when copying this structure, and the one that pays most. It answers "what is this made of" in a grid rather than in paragraphs, because a grid can be compared across repos and prose cannot.
Rows are dimensions, columns are the parts of the system. For a two-part app, columns are the two halves. For a single-part repo, use one column and more rows.
| Frontend | Backend | |
|---|---|---|
| Stack | ... | ... |
| Deploys to | ... | ... |
| Handles | ... | ... |
| Docs | link | link |
Add rows freely: Port, Owns, Talks to, Status. The dimension a reader most wants is usually the one you would have written as an aside.
The architecture diagram#
One diagram, in the README, once. Its job is to answer "what talks to what" before anyone reads a sentence of prose.
Author it by hand as Unicode box-drawing inside a fenced code block, per
~/.claude/conventions/ascii-diagrams.md: characters ┌─┐│└─┘├┤┬┴┼──▶, 78
columns maximum so it never wraps in a narrow view.
Do not use the /diagram skill for this. That skill renders through gum-tui.sh
for a terminal, and terminal-rendered output saved as source is a documented
mistake: it bakes fixed-width alignment and ellipses into a file that should hold
plain markdown. /diagram is for showing someone a diagram in a session. A
README needs source.
Diagram the request or data path, not the directory layout. The tree at the bottom already covers layout, and a box diagram that duplicates it earns nothing.
What to check before you call a diagram done#
A diagram that lines up in your editor can still fall apart where it is read,
because the reader's font decides the width of every character. The docs
browser shipped every diagram in the repo broken for a week (owner, doc 69
D10): the playground loaded JetBrains Mono as Google's "latin" subset, which
carries no box-drawing glyphs, so │ and ─ fell back to SF Mono at a
different advance width and no line met its corner. The fix was a font, not
the diagrams (apps/playground/src/app/layout.tsx, the self-hosted full
face). Check each of these, on the rendered page, not in the source:
- Every character in the diagram comes from one font. Box drawing
(
U+2500toU+257F), the arrow heads▶ ▼ ◀ ▲(U+25B6,U+25BC,U+25C0,U+25B2) and ASCII must share an advance width. In the browser:c = document.createElement("canvas").getContext("2d"); c.font = "12px " + getComputedStyle(pre).fontFamily; ["a", "│", "─", "┌", "▶"].map((ch) => c.measureText(ch).width)must print one number. If it does not, the font stack is the bug: self-host the full face, or put a face that carries box drawing at the same width (Menlo on a Mac) ahead ofui-monospacein the stack. The kit's--font-monotoken is ordered for this. - Only those characters.
┌─┐│└─┘├┤┬┴┼plus the four arrow heads and┈ ╌ ─ ─ ─for dashed edges. Em dashes,→,=>,>and-->drift by font and the em dash is banned here anyway. - Width. 78 columns at most; the docs browser's prose column is about 90 characters at 11.5px and the pre never wraps, so a wider diagram scrolls sideways and the reader loses the right edge.
- Labels inside boxes keep the box closed. Pad each line of a box to the
same length; a one-character overrun pushes
│out of its column and reads as a broken wall even when every font is right. - Render it. Open the page in the playground's docs browser in both themes and look. The kit README's architecture diagram and the toolkit README's module map are the two that read wrong first when any of the above slips, so check those two after any font or renderer change.
Quick Start#
Clone first, then one block per runnable part, with the port in the heading:
**Frontend** (port 5173):Naming the port in the heading is deliberate. It is the single most looked-up fact in the file, and it should be findable without reading a command.
Every command must be one a reader can paste. A command that needs a value they do not have yet gets the acquiring step above it, not a parenthetical.
Documentation#
Group by area when the repo has areas, otherwise one table. Every entry carries a one-line description of what it answers, never just a filename. A bare list of links makes the reader open all of them to find the one they need, which is the work the description was supposed to do for them.
If the docs are published somewhere, that URL leads the section, and the in-repo path follows it.
Repo Structure#
Last, because it is reference rather than orientation. A tree, annotated:
repo/├── frontend/ → Next.js · React 19 · Vercel│ ├── src/app/ · App Router pages + API routes│ └── docs/ui/ · Component documentation│└── backend/ → FastAPI · Python 3.12 · Render └── docs/ · Backend documentation→ annotates a top-level part with its stack and where it deploys. · annotates
a child with what it holds. Blank line between top-level parts. Show only what a
newcomer needs to navigate; a tree that lists everything is a directory listing,
and they already have one.
A note if you are copying from enhancement-product, which uses an em dash for
the child annotation. Do not. The Versable repos run a write hook enforcing a
zero-em-dash budget, and it rejects the file rather than warning, so the em dash
version simply will not save. · is the house substitute.
What is fixed and what is not#
The order, the section names, the title-above-banner rule, the intro table, one architecture diagram, and the tree at the end are all fixed.
Not fixed: the middle. A repo with three deploy targets should have a big Deployment section; a library should have Install and Releasing instead. Section names for things this list does not cover are the repo's own business.
Do not delete content to fit the shape. This structure is for organising what a repo already knows about itself, and content that has nowhere obvious to go belongs in the middle, not in the bin.
Prose do-nots for any top-level doc#
These apply to every README, index and entry document in the repo, not only the root README. Owner, doc 70 E5, 2026-08-19: "the same mistake over and over", named so it can be caught on sight.
- Never describe the doc system instead of the subject. "One of three
doc layers", "the intent layer of the monorepo", "this page is part of the
written canon": a reader opened this file to learn about the thing it
covers, not about where the file sits among other files. The map of the
docs lives in
docs/index.md, once. A doc's first paragraph says what the subject is and what the reader can do with it. - Never claim importance the tree does not back. "The bottom layer of the monorepo", "the foundation every app builds on", "critical": if a package has no consumers yet, say that it has no consumers yet and what it is for. A claim of weight with no file, number or consumer behind it reads as filler, and a reader who checks stops trusting the rest.
- Never open with architecture when the reader came for an action. An index opens with what is in it and which entry to open first; a how-to opens with the first command; a README opens with what the thing is and how to run it. The architecture paragraph comes after the reader can do something.
- Never omit the thing that is live. If a deploy, a site, a registry or a workflow exists and runs, the doc that covers that area names it with its URL or path; a Deploying section that never mentions the live deploy is wrong by omission, which is the hardest wrong to notice from inside.
- Every paragraph carries a fact or an instruction. A paragraph that carries neither (a restatement of the architecture, a sentence about how well organised the docs are, a promise about what a section will do) is cut, not polished.
The check before a doc is called done: read it as the person named in its first paragraph, on their first day, and strike every sentence they could not act on or verify.