README & Example Guidelines (Repo / Module / Example)
How to write README files at three levels — repository, module/package, and example — and what makes a good library example. The conventions here are derived from the D4rt ecosystem, which is the reference exemplar for the whole workspace. Wherever a rule is stated, a concrete D4rt file is cited so you can read the original rather than guess at the shape.
> Scope: this is a global guideline. It governs the prose docs that ship > with
a package (its README.md and example/), not the development docs > under
_copilot_guidelines/ (see documentation_guidelines.md for that > split). README files always stay where they are — never move them.
---
The three levels at a glance
| Level | File | Job | D4rt exemplar |
|---|---|---|---|
| Repository | <repo>/README.md |
The map . Orients a newcomer to the whole ecosystem and routes them to the right package. | tom_ai/d4rt/README.md |
| Module / package | <package>/README.md |
The manual for one publishable unit: what it is, install, features, quick start, usage, architecture, status. | tom_ai/d4rt/tom_d4rt/README.md |
| Example | <package>/example/README.md |
The index to the runnable samples — what each demonstrates and how to run them all. |
tom_ai/d4rt/tom_d4rt/example/README.md
|
A fourth, optional level — the long-form sample / tutorial README — walks a single example project in depth. Exemplar:
tom_ai/d4rt/tom_d4rt_samples/d4rt_introduction_sample/README.md.
A package large enough to have modules adds one more, below the package README: a lib/src/<module>/readme.md
per module, addressed to a contributor working inside that module rather than to a consumer choosing between packages. See
Module-under-lib readmes.
---
Conventions shared by all README levels
These apply everywhere; the level-specific sections below add to them.
1. Attribution blockquote directly under the title. Every D4rt README opens with a short blockquote crediting the project/author. Keep it one or two lines and identical across the repo's packages.
2. Relative sibling links, never absolute paths. Link to neighbours with relative markdown (../tom_d4rt_generator/README.md,
doc/architecture.md). This keeps links working on GitHub, pub.dev, and the local checkout alike.
3. Don't duplicate — link instead. A fact lives in exactly one README. The repo README does not re-explain a package's internals; the package README does not re-explain a sibling's API. When you're tempted to copy, write one sentence and a link. See the "Further documentation" section of
tom_d4rt/README.md ("don't duplicate — follow the link").
4. Tables over prose for enumerable things. Components, features, key types, supported language constructs — all rendered as tables with a stable column shape (e.g.
Package | What it is | Binary). Prose is for concepts; tables are for inventories.
5. ASCII diagrams for structure. Architecture, dependency graphs, and repository/project layout are shown as fenced ASCII trees/diagrams — they render everywhere and diff cleanly. See the two-pass diagram and ecosystem graph in
tom_d4rt/README.md.
6. Progressive disclosure. Order sections from "what is this / why care" → "get it running in 5 lines" → "deep usage" → "architecture" → "status". A reader who stops after the first screen still got value.
7. Runnable code blocks with inline expected output. Any code shown is copy-paste runnable and ends with the expected result as a comment (print(result); // 55). No pseudo-code.
---
1. Repository README
Reference: tom_ai/d4rt/README.md
The repo README is a map, not a manual. Its reader has just arrived and needs to find the one package they actually want. Recommended section order:
1. Title + one-line tagline. "D4rt — a sandboxed Dart interpreter and its ecosystem." The tagline says what the whole repo is in one breath. 2.
Attribution blockquote. 3. Intro that names its own role. State explicitly: "This document is the map; each component has its own README." This sets the reader's expectation that depth lives downstream. 4.
"New here?" pointer to the single best on-ramp (the introduction sample / quick-start package). 5.
"What you can do with …" — a short capability bullet list, outcome- oriented, not feature-oriented. 6.
Conceptual framing section when the repo has more than a handful of packages. D4rt uses "Two execution families" to split the source-based vs analyzer-free worlds
before listing packages, so the component tables make sense. 7. Component tables grouped by category.
One table per category (Interpreter, Bridging, REPL/CLI, Flutter, Conformance, Samples). Columns: Package (linked) | What it is | Binary. 8.
Getting started — the smallest possible runnable snippet(s). 9. Twin/relationship table
if packages pair up (D4rt pairs each source-based package with its analyzer-free twin). 10. Samples learning-path table
— ordered from beginner to advanced. 11. Documentation index table (Topic | Document). 12.
Repository layout — annotated ASCII tree. 13. License link.
Rules specific to this level:
- Every package in the repo appears in exactly one component-table row, linked.
- Do not document any package's API here — one phrase of "what it is" plus the
link is the contract. - The layout tree is annotated (one trailing comment per significant entry), not a bare
tree dump.
---
2. Module / Package README
Reference: tom_ai/d4rt/tom_d4rt/README.md
This is the README a pub.dev visitor reads. It must stand alone. Section order:
1. Title = package name. 2. Attribution blockquote. 3. One-line description
(matches the pubspec description). 4. Overview — how the thing works conceptually, in prose. Enough to decide "is this what I need?". 5.
Installation — the dependencies: yaml snippet and the
dart pub add line, plus a note on SDK / required sibling deps. 6. Features
— grouped sub-sections, each a table. D4rt uses: Language coverage, Standard-library bridges, Bridging, Permission sandboxing. 7.
Quick start — one minimal runnable example with inline // output comments. The reader should be able to paste it and see the comment come true. 8.
Example projects table linking into example/ and any sample projects (Sample | Demonstrates). 9.
Usage — progressive depth, one sub-section per capability (full-script execution, REPL eval, introspection, registering bridges, permissions, helper utilities, config). Each sub-section is a short runnable block. 10.
Architecture — ASCII diagram(s) + prose for the moving parts + a key-types table
(Type | Responsibility). 11. Ecosystem — ASCII dependency diagram showing where this package sits relative to its siblings. 12.
Further documentation — links to the package's own doc/ guides and to related packages, each with a one-line "what's there" and
no duplicated content. 13. Status — the current version, and a description of the suite that states no count.
Rules specific to this level:
- Installation must show the real version constraint style used in the repo;
never instruct a path override or pubspec_overrides.yaml (see dart/coding_guidelines.md
dependency-management rules). - The key-types table is the single best aid for a contributor — keep it current with the actual public types. -
State the version; do not state a test count. The two look alike — a number in the same block — and behave nothing alike. Only one of them survives contact with a repository that is being worked on.
The version is worth stating because a reader acts on it — it says which release the document describes — and a wrong one actively misleads rather than merely disappointing. It is also cheap to hold true: it lives in one line of
pubspec.yaml, so a check is a file read, not a test run. Pin it. In tom_ai/core,
tool/check_readme_status.py does exactly that across every package, and a package suite can pin its own — see
tom_core_server/test/api_reference_coverage_test.dart.
A test count is a claim nothing checks, and an unchecked claim in a document people don't reread is a claim that drifts.
tom_core_server's block reached less than half the truth — 1726 stated against 3491 actual — was corrected in place, and was wrong again within days: 95 suites stated against 103, 20
db-tagged against 24. The mechanism is not carelessness. Every added test is a README edit nobody makes, because nothing connects the two.
Enforcing the count instead is possible — run the suite, compare — and is the wrong trade. It makes every added test a required README edit, and a document test that fails on an unrelated commit gets deleted rather than obeyed. The count also informs nobody: no one picks a dependency on 3491 tests over 1726. Describe the suite instead — how it is run, and anything that gates part of it, such as a tag that skips without a live database. That is what a reader can use, and it does not rot.
---
3. Example README
References: - Canonical-samples case: tom_ai/d4rt/tom_d4rt/example/README.md
- Test-fixtures case: tom_ai/d4rt/tom_d4rt_generator/example/README.md
The example/README.md is a short index, not a tutorial. There are two honest shapes, and you must pick the one that tells the truth about the directory:
(a) Canonical samples — when example/ holds real, user-facing, runnable samples:
1. State that this tree is the canonical home for samples of this domain (D4rt calls it the "P2 canonical home for raw-source interpreter and bridging samples"). 2. A
Sample | Demonstrates table, one row per example file/dir. 3. The run-the-whole-set
line: dart run example/run_all_examples.dart. 4. Cross-links to the other canonical homes (e.g. "Flutter samples live in …", "shell samples live in …") so a reader who's in the wrong place is redirected.
(b) Test fixtures — when example/ actually holds files wired into the test harness rather than teaching samples:
1. Say so plainly: "these are generator test fixtures … not user-facing samples." Don't dress fixtures up as examples. 2. Redirect to the canonical sample homes that do hold user samples.
Rule: never let a fixtures directory masquerade as a samples directory. The redirect in
tom_d4rt_generator/example/README.md
is the pattern to copy.
---
4. Good library examples (the .dart files)
Reference (single example): tom_ai/d4rt/tom_d4rt/example/d4rt_example.dart
Reference (aggregator): tom_ai/d4rt/tom_d4rt/example/run_all_examples.dart
A good example file is itself documentation. Properties, derived from d4rt_example.dart:
1. Self-contained and runnable. A single file with a main() that runs to completion via
dart run example/<name>.dart. No external setup, no args required. 2. One concept per file.
d4rt_example.dart demonstrates exactly one thing — bridging a native class. Name the file for that concept (bridging_classes_example.dart,
permissions_example.dart, eval_example.dart). 3. Minimal imports.
Prefer the package's single public entry point (import 'package:tom_d4rt/d4rt.dart';) over reaching into
src/. 4. Inline expected output. End the meaningful work with the result printed and the expectation as a comment:
print(result); // [Hello, Alice!, Profile of Alice, Guest, Hello from static!]. This makes the example a lightweight test and tells the reader what success looks like. 5.
A short header comment stating what the file demonstrates and the run command (see the top of
run_all_examples.dart). 6. Real, small domain objects. Use a tiny but realistic class (User) rather than
Foo/Bar; concrete names teach better.
Organisation of the example/ directory:
- Group examples into sub-directories by theme (
user_guide/,
bridging_guide/, readme/) once there are more than a handful. D4rt's tom_d4rt/example/
does exactly this. - Provide a run_all_examples.dart aggregator that imports every example's
main, runs each in a try/catch, and reports a pass/fail tally with a non-zero exit on failure (see
run_all_examples.dart). This turns the whole
example/ tree into a smoke test that can run in CI. - Keep the readme/ sub-dir's examples
identical to the snippets pasted into the README, so the README's code is provably runnable.
---
5. Module-under-lib readmes
Reference: tom_ai/core/tom_core_server/lib/src/tomserver/preferences/readme.md
A package with modules carries a readme.md in each lib/src/<module>/. Its reader is a contributor already inside the package who wants to know what this module is for and how to use it — not a consumer choosing between packages, who is served by the package README two levels up.
Four elements are required, and they are the ones that go missing:
1. The attribution blockquote, immediately under the title. These files are read outside the workspace that wrote them. 2.
## Key types — a heading over a Type | Responsibility table. Head it
## Key types & functions when the module ships both. 3. ## Quick start
— a runnable sequence whose stated output is reachable given the preconditions it shows. Where the module needs infrastructure — generated reflection, a registered datasource, a principal — show those as numbered steps rather than omitting them or dropping
main(). Each precondition that fails at a place which does not obviously point at it is exactly the one worth showing. 4.
The Part of footer back to the package README, which owns the module map and the installation. Without it a module readme reached directly is a leaf with no route up.
Why these four and not a longer list. They are what every module readme in the framework already had except one — and that one,
tom_core_server's scheduling, was the longest and most substantive
of its twenty. It was not a thin readme; it was a complete one written to a different shape, most likely before the convention settled. A convention that settles without being written is one a document can miss entirely while reading as finished work.
The ## Key types heading is the clearest illustration of why a structural element is not decoration:
scheduling had its type table, unheaded. It therefore appeared in no document outline, and a reader scanning headings — the only navigation a 150-line readme has — did not find it.
In the Tom workspace, tom_ai/core/tool/check_module_readme_shape.py enforces the four.
---
Checklist
Repo README: - [ ] Tagline + attribution + "this is the map" intro - [ ] Every package linked in exactly one component-table row - [ ] Conceptual framing before the tables (if many packages) - [ ] Samples learning-path + documentation-index tables - [ ] Annotated ASCII layout tree + license link
Module README: - [ ] Title/description/overview/installation/features/quick-start - [ ] Progressive usage sub-sections, all runnable - [ ] Architecture diagram + key-types table - [ ] Further-documentation links with no duplicated content - [ ] Status: version matching
pubspec.yaml, suite described, no count
Example README: - [ ] Correct shape chosen (canonical samples vs test fixtures) and stated honestly - [ ]
Sample | Demonstrates table - [ ] dart run example/run_all_examples.dart line - [ ] Cross-links to other canonical homes
Module-under-lib readme (lib/src/<module>/readme.md): - [ ] Attribution blockquote under the title - [ ]
## Key types heading over a Type | Responsibility table - [ ] ## Quick start
whose stated output is reachable given the preconditions it shows - [ ] Part of [<package>](…)
footer back to the package README
Example .dart files: - [ ] Self-contained, one concept, minimal imports - [ ] Header comment + run command - [ ] Inline
// expected output - [ ] Wired into run_all_examples.dart