# 📦 WP-114 — `docs(site): a MkDocs Material site over docs/`

> Engineering log · [Phase 11 — Rolling, toward v0.5.0](https://borda.github.io/lucid-YOLO/engineering/phase-11-rolling/)
>
> 🔗 Depends on [WP-113](https://borda.github.io/lucid-YOLO/work_packages/wp-113/) · Needed by [WP-114b](https://borda.github.io/lucid-YOLO/work_packages/wp-114b/), [WP-188](https://borda.github.io/lucid-YOLO/work_packages/wp-188/)
>
> ⬅️ [WP-113b](https://borda.github.io/lucid-YOLO/work_packages/wp-113b/) · [Roadmap](https://borda.github.io/lucid-YOLO/ROADMAP/) · [WP-114b](https://borda.github.io/lucid-YOLO/work_packages/wp-114b/) ➡️


## WP-114 — a default list that vanishes when you name one member

<a id="wp-114"></a>

The site publishes what the repository already holds: `mkdocs.yml` owns no prose beyond `docs/index.md`, no plugin generates a page from source, and every register stays a plain markdown file a reader can open on GitHub without the toolchain. The work was configuration, and configuration is where a wrong value reports success.

**Naming one member of a MkDocs default list replaces the whole list.** Supplying `plugins:` at all drops the implicit `search`, so a site whose search box finds nothing builds green; supplying `markdown_extensions:` drops `tables`, and this repository is wall-to-wall pipe tables — the assumption register, the roadmap, every acceptance table would render as literal pipe characters on a build that reports no error. Both are restated explicitly, and `tables` is asserted in `scripts/_tests/test_audit_docs_site.py` rather than trusted to stay listed.

**`--strict` is the whole value of building locally.** It fails on a link to a page that does not exist and on a page the nav never lists. A register nobody can navigate to is one nobody reads, and neither failure raises anything without the flag.

**A second config file needed a second hook instance, for the same reason `docs/` did.** Material's mermaid fence is enabled through a `!!python/name:` tag; MkDocs parses its config with `yaml.Loader` and constructs it, pre-commit's `check-yaml` uses `safe_load` and cannot. So `check-yaml` is now two instances — the default one excluding `mkdocs.yml`, and an `--unsafe` one scoped to it, which still validates the syntax of a 120-line nav without importing anything from it. `mdformat` split the same way and for the same shape of reason: `docs/` is now rendered by a dialect the rest of the tree is not written in, and running the mkdocs plugin over the README or `AGENTS.md` would apply that dialect to files no site renders.

**The audit has to run where the exposure enters.** The docs group is deliberately outside `make setup` and outside `dev`: no gate imports it, and a contributor who never builds the site never installs the tree. The consequence is that no other workflow's environment contains it, so the licence audit — which scans the installed environment rather than the diff (D15, D16) — runs in the docs job, ahead of the build. Measured over the installed tree: 106 distributions, 343 shipped binaries, no GPL-family licence declared, bundled or shipped. One transitive is not permissive and is named rather than buried: `certifi` is MPL-2.0, weak file-level copyleft, reached through `mkdocs-material` → `requests`; MPL-2.0 was already present via `pathspec`, which arrives with mypy.

**`configure-pages` reads by default, and cannot do otherwise with the token CI has.** Its `enablement` input defaults to `false`, and its own `action.yml` states that enabling requires a token other than `GITHUB_TOKEN`. So `pages: read` is the ceiling that job can use rather than a permission it is being denied, and the step is skipped on pull requests: it fails when Pages is not enabled, which would turn every PR red for a reason no PR can fix.

