# ADR 0013: Cross-reference Markdown Files With `[]()` Syntax ## Status Accepted ## Date 2026-06-09 ## Context [ADR-0012](adr-0012-headless-craftos-pc-as-hypothesis-probe.md) and the broader docs/ tree increasingly cross-reference each other, and ADRs reference one another by number. We recently wired `lychee` into `just check` (see the `lint-markdown` recipe in `Justfile`) so broken local links fail the build. Lychee, however, only validates *links it can see*: a bare prose mention of `docs/foo.md` or `ADR-0005`, or a backticked path like `` `docs/foo.md` ``, is invisible to it. When a doc is renamed or moved, those mentions silently rot until a human happens to read the surrounding paragraph. We want renames to break the build, not the docs. ## Decision In every markdown file in the repository, references to other `.md` files must use markdown link syntax `[text](relative/path.md)`. This includes: - Direct file references (`` `docs/install-craftos-pc.md` `` → `` [`docs/install-craftos-pc.md`](docs/install-craftos-pc.md) ``). - ADR-number references (`ADR-0005` → `[ADR-0005](docs/adrs/adr-0005-craftos-pc-harness.md)`, with the path adjusted to be relative to the referencing file). - Plain-prose mentions (`called out in CLAUDE.md` → `called out in [CLAUDE.md](../../CLAUDE.md)`). Excluded by design: - Mentions inside fenced code blocks. They are example/code content, not cross-references, and `lychee.toml` already skips them via `include_verbatim = false`. - Mentions inside inline code spans used purely to *illustrate* the wrong form (as in this ADR's examples above). The reader can see they are placeholders, and lychee does not extract them as links either. - A file's own title (e.g. `# CLAUDE.md` as a heading is not a reference). Link paths are written relative to the file containing the link, so that `lychee --offline` resolves them on the local filesystem with no `--base` indirection. ## Consequences - `just lint-markdown` (and therefore `just check`, pre-commit, and pre-push via [ADR-0011](adr-0011-git-hooks-own-commit-push-verification.md)) catches dead cross-references the moment a doc is moved or renamed. - The convention is *social* — humans and agents must remember to apply it when writing prose. Lychee enforces correctness only once a link exists; it cannot flag a mention that should have been a link but wasn't. - The `[`path`](path)` style (backticked path as link text) is preferred for direct file references, matching the existing house style in [`docs/README.md`](../README.md) and [`docs/adrs/README.md`](README.md). For ADR mentions in flowing prose, `[ADR-####](path)` reads better than the backticked form. ## Future Work - If rot reappears anyway, add a small grep-based lint that flags unbracketed `.md` and `ADR-####` mentions and wire it into `just check`. Deferred until there is evidence the social convention is insufficient.