- Describe what a thing does, not how it came to be. Git carries history.
- Everything a reader needs resolves inside a fresh clone. No pointers to files or labels that exist only on one machine.
The pipeline
Data flows in one direction. Each stage reads the stage above and writes for the stage below.scripts/ (research, validation, reference generation), docs/ (this site), infra/ (Terraform), data/ (artefacts), agents/.
This order is the canonical reading order. Layer README footers link upstream and downstream so the tree can be walked end to end.
Writing rules
Describe the present
State what the code is and does. Do not narrate the process that produced it.Banned
“Phase 5”, “Sprint 0”, “Wave 0”, “Step 3 of”, “recently added”, “now complete”, “newly”, “deprecated alias for”
Fine
Ordered instructions for a genuine procedure that is a how-to, not a history. The test: does the number describe what the reader does next (keep) or what stage the project reached (delete)?
Every reference resolves in a clone
A reader has the committed tree and nothing else. Never point at something they cannot open.- Banned: links to
_roadmap/**,_issue_logs/**, or labels whose definition is not committedAD-3,Appendix C,R-3,F9, and the like. - If a decision is worth citing, state the decision inline. Replace “self-hosted bundle so COEP doesn’t block assets (AD-11)” with the reason itself.
- Describing a gitignored directory as gitignored is allowed. The rule bans depending on its contents, not naming its existence.
Counts live behind a gate
A number written by hand rots. Cite a count in prose only when a drift gate keeps it honest (the repo gates several viascripts/docs_facts.py and scripts/app_docs_audit.py). Otherwise describe the structure without the literal number, or point at the generated artefact.
One vocabulary
The same concept gets the same heading everywhere. The section names in the layer README contract below are the canonical names use them verbatim.Layer README contract
Every pipeline layer (ingestion/, transform/, ml/, app/) carries a README.md with these sections, in this order. Omit a section only when it is genuinely empty; never reorder or rename.
Title + purpose
# <dir>/ <one line>. A single sentence: what the layer reads and what it produces.What it does
One short paragraph. The layer’s job in the pipeline, naming the upstream input and downstream consumer.
Layout
The directory/file map, each entry with a one-line role. A DAG or data-flow diagram is a subsection here, not a separate top-level heading.
Inputs and outputs
The concrete upstream source and downstream consumer, and the contract between them which tables/files/columns cross the boundary.
Contracts
What this layer guarantees that something downstream relies on: enforced schemas, numerical tolerances, determinism, invariants. Omit if none.
Commands
The
make/CLI targets to build, test, and run, one line each.Tracked vs generated
What is committed versus regenerated, and the command that regenerates it. Omit if the layer has no generated artefacts.
Tests
What is covered and how to run it.
← upstream: <dir>/ · downstream: <dir>/ →
Layer directory contract
- A layer’s own code lives under a predictable root (
src/for Python layers,src/for the app,models/for dbt). - Tests live in
tests/beside the code they cover. - Generated artefacts are gitignored and reproducible from a single command named in Tracked vs generated.
- File and directory names describe their contents in the project vocabulary. A new app feature directory is its canonical slug, reused verbatim as its docs filename.