Publish docs¶
Status: Current. Version: 0.1 Date: 2026-08-11
This site is markdown in docs/, built by MkDocs Material,
validated by GitHub Actions, and deployed by Cloudflare Pages to
docs.kovent.fordsworth.com.
Local preview¶
./scripts/docs.sh serve # http://127.0.0.1:8000, live reload
./scripts/docs.sh build # strict production build into site/
./scripts/docs.sh install # create .venv-docs and install the toolchain
./scripts/docs.sh clean # remove site/ and .venv-docs/
The script creates a local virtualenv at .venv-docs on first run. Both site/ and .venv-docs/
are gitignored.
Override the port with DOCS_PORT=8123 ./scripts/docs.sh serve.
The pipeline¶
Edit docs/ or mkdocs.yml
│
├─► Open PR ──► GitHub Actions (.github/workflows/docs.yml)
│ pip install -r requirements-docs.txt
│ mkdocs build --strict
│ must pass before merge
│
└─► Merge to main
│
├─► GitHub Actions runs again (validation only)
│
└─► Cloudflare Pages (git hook)
pip install + mkdocs build
deploy → docs.kovent.fordsworth.com
usually 1–3 minutes
GitHub Actions does not deploy. It is the quality gate; Cloudflare Pages is the deployer.
Why --strict matters¶
Strict mode turns warnings into errors, so the build fails on a broken internal link, a nav entry pointing at a missing file, or a page absent from the nav. Because the whole site is cross-referenced, this is the mechanism that stops the documentation rotting as pages move.
If CI fails on a link you believe is correct, check that the path is relative to the file, not to
docs/. A page in docs/platform/ linking to an engine needs ../engines/registry.md.
Cloudflare Pages setup¶
One-time, for whoever provisions the project.
| Setting | Value |
|---|---|
| Git repository | fordsworth-associates/kovent |
| Production branch | main |
| Framework preset | None |
| Root directory | / — mkdocs.yml is at the repository root |
| Build command | pip install -r requirements-docs.txt && mkdocs build |
| Build output directory | site |
| Environment variable | PYTHON_VERSION = 3.12 |
Set PYTHON_VERSION on both the production and preview environments, or preview builds fall
back to the build image default and can resolve a different MkDocs Material version.
Once linked, every push to main deploys to production and every pull request gets its own preview
URL, so a docs change can be read in its rendered form before merge.
Scripted alternative¶
The project can be created through the API instead of the dashboard. This requires the Cloudflare
GitHub App to already be installed and authorised on the fordsworth-associates organisation — if it
is not, do the first link in the dashboard, because the API cannot grant that consent.
export CF_ACCOUNT_ID=... # Cloudflare dashboard → Workers & Pages → Account ID
export CF_API_TOKEN=... # token with the "Cloudflare Pages: Edit" permission
curl -sS -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"name": "kovent-docs",
"production_branch": "main",
"build_config": {
"build_command": "pip install -r requirements-docs.txt && mkdocs build",
"destination_dir": "site",
"root_dir": ""
},
"deployment_configs": {
"production": { "env_vars": { "PYTHON_VERSION": { "type": "plain_text", "value": "3.12" } } },
"preview": { "env_vars": { "PYTHON_VERSION": { "type": "plain_text", "value": "3.12" } } }
},
"source": {
"type": "github",
"config": {
"owner": "fordsworth-associates",
"repo_name": "kovent",
"production_branch": "main",
"deployments_enabled": true,
"pr_comments_enabled": true
}
}
}'
Then attach the custom domain:
curl -sS -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/kovent-docs/domains" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"name": "docs.kovent.fordsworth.com"}'
wrangler pages is not used here. It provisions direct-upload projects and cannot bind a Git
repository, which is the whole point of this setup.
DNS¶
The subdomain is a CNAME to the Pages project, configured in the cPanel Zone Editor. Nameservers stay with the host; only this subdomain points at Cloudflare.
| Record | Name | Target |
|---|---|---|
| CNAME | docs.kovent |
<project>.pages.dev |
Add docs.kovent.fordsworth.com as a custom domain in the Pages project first, then create the CNAME.
Cloudflare provisions the certificate in 5–30 minutes.
Order matters. Creating the CNAME before the custom domain is registered on the project gets a
Cloudflare error page rather than the site, because Pages routes by the Host header and will not
recognise a hostname it has not been told to serve.
The site is marked noindex, nofollow in mkdocs.yml because it is internal and contains
unconfirmed regulatory positions. For access control beyond that, put Cloudflare Zero Trust Access in
front of the subdomain.
Adding a page¶
- Create the markdown file in the right section directory
- Add it to
nav:inmkdocs.yml— strict mode fails if a page exists outside the nav - Open with an
h1, then akovent-eyebrowdiv naming the category - Add a status blockquote if the page carries doctrine or a specification:
> **Status:** Drafted | Specified | Locked | Living document
> **Version:** 0.1
> **Date:** YYYY-MM-DD
- Close with a
## Relatedsection linking outward - Run
./scripts/docs.sh buildbefore pushing
For return specifications, follow how to write a return spec rather than inventing a structure.
House style¶
| Rule | Reason |
|---|---|
| Status markers ✅ 🟡 ⬜ mean prototyped, designed, not started | Consistent across every engine and spec page |
| Nothing is marked proven | There is no runtime yet, and overclaiming is the fastest way to lose a reader's trust |
| Every rule and threshold carries a citation | Same law the platform enforces |
| Open questions are named, not hidden | A guess in a spec is a future defect |
| Diagrams are ASCII in fenced blocks | Diffable in review, no toolchain, no stale exported images |
| Tables for enumerable facts, prose for reasoning | A table of arguments is unreadable |