# Documentation site

The documentation site is built with **[VitePress](https://vitepress.dev/)** and **[VitePress Theme +](https://vitepress-theme-default-plus.lando.dev/)**. Markdown source and site configuration both live in `docs/` (config in `docs/.vitepress/config.ts`), and build output goes to `docs/.vitepress/dist/`.

## Local development

```bash
npm ci
npm run docs:dev
```

The dev server starts on `http://localhost:5173/docs/`. Submodules (e.g. `experiments/`) are initialized automatically before the dev server starts -- no manual `git submodule` step needed.

## Building

```bash
npm run docs:build
```

The `docs:build` script runs `git submodule update --init` and then `mvb docs`, which builds versioned documentation for each qualifying git tag plus a `dev` build from the current tree. Before each sub-build, `mvb` writes that version's semantic version into `package.json` in a temp checkout; `config.ts` reads `.version` for the sidebar switcher label (falling back to `"dev"` when the field is absent, as in a local `vitepress` run). CI sets `VPL_MVB_BRANCH` to the current commit SHA so `mvb` knows which ref to treat as the development head.

```bash
npm run docs:preview
```

## How it works

- `docs/` contains all markdown content, organized by section (agents, guides, ADRs, etc.)
- `docs/.vitepress/config.ts` defines the sidebar navigation and markdown processing. After the HTML build, it copies each page's `.md` source into `docs/.vitepress/dist/` next to the matching `.html` (README files land as both `index.md`, matching the HTML URL, and `README.md`, so in-document `README.md` links still resolve) so the same URL plus `.md` is a static file. Relative images those pages reference (`![](...)` and `<img src>`, for example `docs/agents/icons/*.png`) are copied to the same relative path so they resolve next to the `.md`; VitePress still hashes those images in the HTML build. Indexable HTML pages advertise the markdown file with `<link rel="alternate" type="text/markdown">`. See [`site-deployment.md`](site-deployment.md).
- `getMarkdownFiles()` auto-discovers markdown files and walks nested directories for dynamic sidebar sections (ADRs, experiments, design docs, specs, plans). Nested folders become nested sidebar groups; a subdirectory README supplies the group's title and link.
- Symlinks connect submodule content into `docs/` (e.g. `docs/experiments` -> `../experiments`)
- The `search.options.scopes` array in `config.ts` defines the scope pills shown in the search modal. Each scope has a `label` and a list of `prefixes` (path prefixes like `/docs/guides/`). When a user activates a scope, search results are filtered to pages whose path starts with one of the scope's prefixes. Every `docs/` subfolder that produces rendered pages must appear in at least one scope; otherwise its pages become unreachable when any scope pill is active.
- `multiVersionBuild` at `docs/.vitepress/config.ts` controls which versions are to be built. `getLatestPatchMatching(gitVersionTags(MVB_TAG_MATCH), ">=0.37.0")` lists git tags with the shared `MVB_TAG_MATCH` glob (`v[0-9].*`, also set as `multiVersionBuild.match`), keeps those that satisfy the given range, and builds a range-set of those exact latest-patch versions. That is required because mvb filters each tag with `semver.satisfies` independently — it cannot pick "latest per minor" itself. `sidebarEnder` sets up the version switcher with a few versions and the page `/v/index.md` contains a more comprehensive list of versions.
- Multi-word search queries use **AND** semantics — all terms must appear on a page for it to match. Wrapping words in double quotes (e.g. `"eval scenario"`) enables exact-phrase matching: only pages containing the quoted words adjacent and in order are returned.
- VitePress always emits `404.html` (synthetic `404.md`; not configurable). Cloudflare serves the nearest `404.html`, so that file is used for misses under `/docs/`. See [`site-deployment.md`](site-deployment.md).
- VitePress is not designed for multiversion in mind, this + the cached versions required to speed up the process (VitePress is very slow) make that older cached builds have their version list wrong. This limitation is accepted
while a better solution is developed.

## Submodules

Some doc content lives in separate repositories linked as git submodules:

| Submodule | Path | Docs symlink |
|-----------|------|-------------|
| [fullsend-ai/experiments](https://github.com/fullsend-ai/experiments) | `experiments/` | `docs/experiments` -> `../experiments` |

The `docs:dev` and `docs:build` scripts in the root `package.json` handle submodule initialization automatically. CI checkout in `.github/workflows/site-build.yml` uses `fetch-tags: true` and `fetch-depth: 0`; `git submodule update --init` runs in the build step.

## CI/CD

- **`.github/workflows/site-build.yml`** — builds the VitePress site on PRs and pushes to `main`, uploads the artifact
- **`.github/workflows/site-deploy.yml`** — deploys the built artifact to Cloudflare Workers on `main` pushes, uploads preview versions on PRs

For Cloudflare Worker setup and troubleshooting, see [`site-deployment.md`](site-deployment.md).
