Documentation System
This file describes how the MkDocs documentation site is built, so that AI agents can understand the system and make changes confidently.
Overview
The site is built with MkDocs using the Material for MkDocs theme. Documentation is NOT authored directly in the docs/ directory. Instead, a build script (scripts/build_docs.sh) assembles markdown files from source locations across the repo into docs/, and then mkdocs build generates the static site into site/.
Key files
| File | Purpose |
|---|---|
mkdocs.yml |
MkDocs configuration (theme, plugins, markdown extensions) |
scripts/build_docs.sh |
Assembles docs/ from source files across the repo |
scripts/generate_dbt_docs.py |
Converts dbt YAML schemas into markdown pages |
scripts/resolve_snippets.py |
Resolves --8<-- snippet directives for Hex guides upload |
hex_context.config.json |
Configures which files are uploaded as Hex guides |
.github/workflows/hex_context_toolkit.yml |
GitHub Action that publishes guides to Hex on merge to main |
docs/.pages |
Top-level sidebar navigation ordering (awesome-pages plugin) |
docs/dbt/.pages |
Navigation ordering within the dbt section |
docs/index.md |
Site homepage (static, checked into repo) |
docs/how_to_document.md |
User-facing guide on how to add documentation (static, checked into repo) |
Build process
Running bash scripts/build_docs.sh does two things:
1. Query docs
The script iterates over immediate child directories of queries/ (these are sections, e.g. engagement_and_usage). For each section, it recursively finds all .md files (at any depth) and copies them flat into docs/queries/<section>/. Any subdirectory structure is discarded.
Markdown files can live directly in the section directory or inside a query subdirectory — both are supported:
queries/
engagement_and_usage/ <-- section
call_log_count/ <-- query subdirectory (optional)
call_log_count.md <-- copied
call_log_count.sql <-- NOT copied (only .md files)
network_growth_and_health/ <-- section
network_size.md <-- copied (directly in section)
network_size.sql <-- NOT copied
daily_calls.md <-- copied
Result in docs/:
docs/queries/
engagement_and_usage/
call_log_count.md
network_growth_and_health/
network_size.md
daily_calls.md
In the sidebar, this renders as:
2. dbt docs
The script runs scripts/generate_dbt_docs.py, which reads dbt/models/_sources.yml and dbt/models/_models.yml and generates two markdown files:
docs/dbt/sources.md— one section per source, with tables listing columns, types, and descriptionsdocs/dbt/models.md— one section per model, with tables listing columns, types, descriptions, and tests
The Python script uses yaml.safe_load to parse the YAML and produces markdown with ## headings per source/model and ### headings per table, with pipe-delimited markdown tables for columns.
Static pages
docs/index.md and docs/how_to_document.md are checked directly into the repo and are not generated by the build script. They persist across builds.
Navigation
Sidebar ordering is controlled by .pages files (via the awesome-pages MkDocs plugin), NOT by a nav: key in mkdocs.yml. The top-level docs/.pages defines the order of top-level sections. Subdirectories can have their own .pages files (e.g. docs/dbt/.pages).
Currently docs/.pages contains:
Markdown extensions
Configured in mkdocs.yml:
- pymdownx.snippets (
base_path: .): Allows embedding file contents inline using--8<-- "path/from/repo/root". This is how query.mdfiles include their SQL source. Paths are relative to the repo root. Note: the snippets preprocessor runs on raw markdown before rendering, so--8<--directives are processed even inside code fences. - pymdownx.highlight + pymdownx.superfences: Syntax highlighting for code blocks.
- tables: Markdown table support.
- admonition + pymdownx.details: Callout boxes (warnings, tips, etc.).
Building and previewing
# Assemble docs from repo sources
bash scripts/build_docs.sh
# Build static site (output: site/)
mkdocs build --strict
# Or preview locally with live reload
mkdocs serve
Deployment
The site deploys to Cloudflare Pages. The Cloudflare build command runs the full pipeline:
pip install mkdocs-material mkdocs-awesome-pages-plugin && bash scripts/build_docs.sh && mkdocs build --strict
Build output directory: site
site/ is in .gitignore — Cloudflare builds it fresh on each deploy.
Hex guides
Query and utility documentation is also published as guides to Hex (our BI tool), giving Hex's AI context about our queries. This is handled by a separate GitHub Action (.github/workflows/hex_context_toolkit.yml) that runs on merge to main.
Because Hex receives raw markdown (not rendered HTML), the --8<-- snippet directives must be resolved before upload. The pipeline works as follows:
scripts/resolve_snippets.pyreads.mdfiles fromqueries/**/*.mdanddocs/utilities/**/*.md, inlines any--8<--referenced files, and writes resolved markdown to.hex_guides/.- The
hex-inc/action-context-toolkitaction uploads all.mdfiles from.hex_guides/to Hex.
The .hex_guides/ directory is ephemeral (created only in CI, listed in .gitignore). The Hex config (hex_context.config.json) points at this directory.
This is completely independent of the MkDocs/Cloudflare pipeline — both consume the same source .md files but process them differently.
Important gotchas
- Do not author docs directly in
docs/queries/— they will be overwritten bybuild_docs.sh. Query docs live inqueries/<section>/<query_name>/. - Snippet paths must match the actual file location in the repo. If a query is moved to a different section, the
--8<--path in its.mdfile must be updated. This affects both the MkDocs site and the Hex guides. - The
--8<--snippet directive cannot be displayed as example text in a code block because the snippets preprocessor runs before markdown rendering. To show the syntax to users, use HTML with-to break the pattern (e.g.<pre><code>--8<-- "path"</code></pre>).