# Documentation

**Last full review: 2026-08-08.**

Documentation comes in five kinds, each with a different audience and lifetime. Four live under `docs/`; the
fifth is deliberately outside the repository.

| Kind | Where | Audience | Lifetime |
|------|-------|----------|----------|
| Human docs | `docs/*.md` | the developer | permanent |
| LLM wiki | `docs/wiki/` | Claude only | permanent, continuously refreshed |
| ADR | `docs/adr/` | both | immutable once accepted |
| Plans | `docs/plans/` | both | deleted when the task closes |
| Private context | `CLAUDE.local.md` | Claude only | gitignored, never committed |

## Rule 0 — verify before you write

**Never document from memory or from inference. Check the code first.**

Applies to every kind below, the wiki included.

- A console command, route, file path, class, method, constant or config key goes into a document **only after
  it has been confirmed to exist**. `grep` or open the file — do not reconstruct it from a naming convention.
- A behavioural claim gets read out of the implementation, not guessed from the class name.
- No plausible-sounding filler. If it is not verified, it does not go in.
- When a document and the code disagree, **the code wins** and the document is wrong — fix it in the same pass.

**Why:** the documentation this set replaces stated that database columns are `snakeCase`. They are camelCase,
and have been since the first migration. A rule that is wrong is worse than a rule that is missing, because it
is followed.

## 1. Human documentation

Read by the developer, kept at the top level of `docs/`. Linked from [README.md](../README.md).

| File | Content |
|------|---------|
| [INTRODUCTION.md](INTRODUCTION.md) | what the project is, tech stack |
| [STRUCTURE.md](STRUCTURE.md) | application structure, entry points |
| [DEVELOPMENT.md](DEVELOPMENT.md) | local Docker environment, assets, console commands |
| [ADMIN.md](ADMIN.md) | admin backend, rights |
| [CRON.md](CRON.md) | scheduled tasks |
| [DEPLOYMENT.md](DEPLOYMENT.md) | server deployment (run by the developer) |

Plus the process documents, which bind Claude as much as the developer:

| File | Content |
|------|---------|
| [WORKFLOW.md](WORKFLOW.md) | the cycle every task follows |
| [CODE_STYLE.md](CODE_STYLE.md) | principles and formatting |
| [TESTING.md](TESTING.md) | the unit test suite, when it runs, what it covers |
| [VERIFICATION.md](VERIFICATION.md) | how a change is proved, and what only the developer can judge |
| [COMMIT.md](COMMIT.md) | commit messages, what may go in a commit, when to push |
| DOCUMENTATION.md | this file |

## 2. LLM wiki — `docs/wiki/`

**Claude writes this for itself. No human reads it.** Its only purpose is that Claude can understand the
codebase as fast and as completely as possible, without re-deriving the same knowledge on every task.

Based on the [LLM wiki pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f).

**Layers**

- **Raw source** — the code. Immutable truth. The wiki never replaces it.
- **The wiki** — Claude-generated markdown: subsystem pages, entity pages, concept pages, cross-linked.
- **The schema** — `CLAUDE.md` plus this file: how the wiki is organized and maintained.

**Operations**

- **Ingest** — after understanding a piece of the codebase, write it down and update every related page in
  the same pass.
- **Query** — answer from the wiki first, verify against the code, then go to the code only if the wiki is
  silent or stale.
- **Lint** — during the weekly review: find contradictions, stale claims, orphan pages and missing
  cross-references.

**Every page carries a freshness header.** The date at the top of this file covers the *set*; it says nothing
about a single page. Without a per-page signal, a page written three weeks ago against since-changed code
looks exactly like one verified this morning, and the weekly lint degenerates into "re-read all twenty".

```markdown
<!--
	covers: models services modules/admin
	verified: <short sha>
-->
```

- **`covers:`** — space-separated pathspecs, relative to the repository root, that the page describes.
  Directories where the page is about a directory (`services`, `widgets`, `modules/admin`), files where it is
  about specific ones. Every path must exist. A page whose `covers:` is deliberately broad —
  [wiki/GOTCHAS.md](wiki/GOTCHAS.md) is cross-cutting — will report stale often. That is correct: it is the
  page most likely to rot.
- **`verified:`** — the commit the page's claims were last read against. Set it to `HEAD` **after** re-reading
  the page against the code, never as a formality.

```shell
./bin/wiki-stale        # STALE / BROKEN / ok per page; exit 1 if anything needs work
```

The script diffs `covers:` between `verified:` and `HEAD`. It runs at task close
([WORKFLOW.md](WORKFLOW.md#5-closing-the-task)) and it is the first step of the weekly review below.

**Rules**

- **Rule 0 applies hardest here.** The wiki is written fast, in bulk, about code that is not open in front of
  you — which is exactly when memory substitutes itself for fact. Grep the symbol, open the file, *then* write
  the sentence. A wiki that is 95% right is a liability: it will be trusted on the 5%.
- Optimize for *density*, not prose. Tables, file paths, call chains, gotchas.
- Always cite the file (`path/to/File.php:123` where a line matters).
- Record what is *not* obvious from reading a single file: conventions, invariants, cross-cutting behaviour,
  traps.
- Never document line-by-line what a class does — that is what the class is for.
- Update the wiki **after** the implementation is approved, never during it.
- A claim that turns out to be wrong gets deleted, not annotated.

## 3. ADR — `docs/adr/`

**Every decision point gets an ADR.** Architecture, library choice, data model, convention — if there was a
choice and we picked one option, it is written down with its rationale and its consequences.

Format and index: [adr/README.md](adr/README.md).

## 4. Plans — `docs/plans/`

Living documents for work in progress. Created in plan mode, updated during implementation, **deleted when
the task is approved and closed**. Format: [plans/README.md](plans/README.md).

## 5. Private context — `CLAUDE.local.md`

Everything that must not enter the repository: who the developer is, who the client contacts are and how to
write for them, environment quirks specific to one machine, anything credentials-adjacent.

**Rules**

- The file is **gitignored**. Nothing from it gets copied into a committed document.
- This is the **only** place for private project context. Do not use the assistant's memory store for this
  project — a single, visible, editable file the developer owns beats an invisible one.
- Keep it short and factual. It is loaded on every session, so bloat costs context on every task.
- When something in it turns out to be wrong or obsolete, edit or delete it there.
- Personal names, roles and client-relationship notes belong here, not in `docs/`.

## The sibling project

**Molino** — the developer's sibling project, checked out next to this one — is built on the same codebase,
for the same client, and carries the same documentation
set. When a convention here is unclear, its equivalent there is a useful cross-check — but **it is not
authoritative for this repository.** The two have diverged: Molino serves nine brands from one deployment,
this one serves a single site; Molino has no rental, billing or gate-control domain at all. Copying a claim
across without verifying it here violates Rule 0.

## Weekly review

If more than a week has passed since the date at the top of this file, run a review before starting new work:

1. **Run `./bin/wiki-stale` first.** It names the pages the code has outgrown; those get re-read against the
   code and their `verified:` line bumped. The pages it reports as `ok` are not re-read — that is the point of
   the header.
2. Read every document under `docs/`, plus `CLAUDE.md` and `CLAUDE.local.md`.
3. Reorganize, condense, update, delete.
4. Lint the wiki (contradictions, orphans, missing links) — the script finds staleness, not contradictions.
5. Verify the human docs still match reality (commands that exist, paths that exist).
6. Update the review date here.
