132 lines
6.6 KiB
Markdown
132 lines
6.6 KiB
Markdown
# The documentation convention
|
||
|
||
How this repository keeps its own docs from lying. Read before writing or editing any `.md`.
|
||
|
||
> Last verified: 2026-07-30 against commit `d3ec723`.
|
||
|
||
---
|
||
|
||
## 1. One home per fact
|
||
|
||
Three trees, and a fact belongs to exactly one of them.
|
||
|
||
| Tree | Answers | Example |
|
||
| --- | --- | --- |
|
||
| [`product/`](../../product/index.md) | **What the business is** | escrow holds funds until check-out is confirmed |
|
||
| `docs/` | **What we built, and how we work** | the escrow ledger is implemented; here is how to test it |
|
||
| `archive/` | **How we got here** | the phase-10 prompt that built the ledger, and its report |
|
||
|
||
If you are about to write a business rule into `docs/`, it belongs in `product/`. If you are about to
|
||
obey something in `archive/`, stop — it is a record, not an instruction.
|
||
|
||
Two documents stay outside `docs/` on purpose:
|
||
|
||
- [`DEPLOY.md`](../../DEPLOY.md) — the deploy *procedure*, at the repo root where an operator will look.
|
||
- The three `CLAUDE.md` files — the hard-rule tier. See [rules/index.md](index.md).
|
||
|
||
Inside `docs/`, each section owns one question:
|
||
|
||
| Section | Owns |
|
||
| --- | --- |
|
||
| [`rules/`](index.md) | What must never be broken |
|
||
| [`integration/`](../integration/index.md) | The client↔server seam: contract, config, topology, OpenAPI |
|
||
| [`flows/`](../flows/index.md) | What is implemented and how to test it, one file per user journey |
|
||
| [`status/`](../status/index.md) | Where the project actually is: implemented, backlog, decisions |
|
||
| [`roadmap/`](../roadmap/index.md) | Where it goes next, and what gates a launch |
|
||
|
||
`product/` is a **structured docs tree** with a generated HTML view: the `.md` files are canonical, the
|
||
matching `.html` files are built by `cd product && node build-docs.mjs`. Edit the Markdown and
|
||
regenerate — never hand-edit the HTML. If you add or rename a `.md`, update the `NAV` manifest in
|
||
`product/build-docs.mjs` in the same change.
|
||
|
||
---
|
||
|
||
## 2. What to update when X changes
|
||
|
||
This is the anti-drift contract. Each row is enforced by review, and (from phase 7) warned about by the
|
||
pre-commit hook.
|
||
|
||
| When you change… | Update, in the same change |
|
||
| --- | --- |
|
||
| An endpoint's route, shape, or status codes | [`docs/integration/`](../integration/index.md) + the OpenAPI snapshot |
|
||
| A project, layer, route group, provider, or major folder | The matching **architecture section** — see §3 |
|
||
| A user-facing flow, so it now works end to end | `docs/flows/<flow>.md` — what it does, and how to test it |
|
||
| A backlog item, so it is now done | **Tick it** in `docs/status/backlog.md`. Never delete a row — a ticked row is the record that it shipped |
|
||
| A decision that isn't derivable from the code | `docs/status/decisions.md` — the decision, the date, and why |
|
||
| A business rule you discovered or decided | The relevant `product/**.md`, then regenerate the HTML. Record decisions; do not invent rules |
|
||
| A rule, so it now says something different | The one file that owns it (see [rules/index.md](index.md)) — not a second copy elsewhere |
|
||
| A new reusable pattern, seam, base class, or hook family | A short note in the reference file for that area, so the next change reuses it instead of reinventing it |
|
||
| A mock or deferred external service | `docs/status/` — the seam (interface + file), what is faked, why, the config keys it reads, and step-by-step how to make it real |
|
||
|
||
**A mock is only sanctioned behind a DI-registered interface.** Mock and real implement the same
|
||
interface; selection is by configuration, never by an `if (mock)` scattered through the code. An
|
||
unrecorded mock is a defect.
|
||
|
||
---
|
||
|
||
## 3. Architecture sections are canonical, and there are exactly three
|
||
|
||
| Level | Canonical section |
|
||
| --- | --- |
|
||
| Repo | **"Repository layout"** in [root CLAUDE.md](../../CLAUDE.md) |
|
||
| Frontend | **"Project structure"** in [client/CLAUDE.md](../../client/CLAUDE.md), expanded in [client/structure.md](client/structure.md) |
|
||
| Backend | **"Project map"** in [server/CLAUDE.md](../../server/CLAUDE.md), expanded in [server/structure.md](server/structure.md) |
|
||
|
||
A map is only canonical if it stays accurate. **Stale instructions are worse than none** — an agent
|
||
that trusts a wrong map spends its budget in the wrong place and lands a change in the wrong layer.
|
||
|
||
An architecture section describes **patterns and boundaries**, not a file listing. If it wants to grow a
|
||
line per file, that is the signal it has stopped being a map: describe the shape of a route group, not
|
||
each page inside it. `git ls-files` already lists files, for free, and never goes stale.
|
||
|
||
---
|
||
|
||
## 4. `Last verified`
|
||
|
||
Every doc that makes a **claim about the current state of the code** carries, directly under its title:
|
||
|
||
```
|
||
> Last verified: <YYYY-MM-DD> against commit <short-sha>.
|
||
```
|
||
|
||
A doc without one is a claim, not a fact.
|
||
|
||
| Must carry it | May omit it |
|
||
| --- | --- |
|
||
| Everything in `docs/rules/`, `docs/status/`, `docs/flows/`, `docs/integration/` | Index/README files that only link onward |
|
||
| Any doc quoting a line count, a file count, a config key, or a default value | `product/` (business truth, not code state — it carries its own decision dates) |
|
||
|
||
Two companion rules:
|
||
|
||
- **Verify, don't copy.** A load-bearing claim is checked against the code or against a run before it is
|
||
written down. Re-stamping the date without re-checking is the failure mode this is designed to catch.
|
||
- **If it cannot be checked, mark it.** Prefix the sentence with `UNVERIFIED:` and say what would settle
|
||
it. An honest gap is useful; a confident guess is not.
|
||
|
||
---
|
||
|
||
## 5. Length budgets
|
||
|
||
So this doesn't regrow into the thing it replaced.
|
||
|
||
| Tier | Budget | If it overflows |
|
||
| --- | --- | --- |
|
||
| A `CLAUDE.md` | **250 lines** | Something in it is reference, not a hard rule. Move it. |
|
||
| A hard-rule list | **15–25 numbered items** | The weakest items aren't hard rules. Cut them. |
|
||
| A `docs/rules/**` reference file | **400 lines** | Split by sub-topic, or you are listing where you should be describing. |
|
||
| A `docs/flows/<flow>.md` | **200 lines** | It is covering two journeys. |
|
||
|
||
The rule behind the numbers: **loading a rule must not cost 40k tokens.** That is what the old
|
||
1,098-line `client/CLAUDE.md` did to every frontend change, and it is why nobody read past the top.
|
||
|
||
---
|
||
|
||
## 6. Style
|
||
|
||
- **English throughout**, including in files that describe Persian UI copy. Quote the Persian, explain in
|
||
English.
|
||
- Prose over bullet soup for reasoning; tables for anything with more than three parallel cases.
|
||
- Link, don't restate. Two copies of a rule drift; one copy and a link cannot.
|
||
- Write the *why* down when it isn't obvious from the rule. A rule whose reason is recorded survives
|
||
contact with a case it didn't anticipate; a bare prohibition gets worked around.
|