Docs publishing
The documentation site is a Docusaurus site. The
prose lives at the repository root in docs/, next to the code it documents;
website/ holds only the site machinery (config, React components, styles), so
the Python project root stays free of Node tooling.
How it is published
Everything is driven by the docs.yaml
workflow:
| Trigger | What happens |
|---|---|
Pull request touching docs/ or website/ | Type-check and build, docs/ included — a broken link or a bad MDX page fails the PR. Nothing is published. |
Push to main touching website/ | Build the released versions only and publish them to GitHub Pages. A push that only changes docs/ deploys nothing: visitors would see no difference. |
| Manual Run workflow | Same as a push to main. |
Publishing uses the GitHub Actions Pages source, so there is no gh-pages
branch and no bot commits in the history. The artifact the build produces is
handed straight to actions/deploy-pages.
Reviewing a pull request's docs
GitHub Pages allows exactly one deployment per repository, so a pull request
cannot have its own preview URL without giving up the Actions publishing source
and going back to a gh-pages branch. Instead, every PR build attaches the
finished site to the workflow run:
- Open the PR's Docs check → Summary → Artifacts →
docs-site-pr-<number>. - Unpack it over
website/build. cd website && npm run serve.
Use npm run serve rather than a generic static server: the site is published
under /ha-power-insight/, and a server rooted elsewhere will 404 every asset.
If preview URLs ever become worth the setup, an external host (Cloudflare Pages, Netlify) is the way to get them — it builds fork PRs in its own sandbox, which is the part that is genuinely awkward to do safely from this repository.
Settings → Pages → Build and deployment → Source: GitHub Actions.
This replaced the old Deploy from a branch → gh-pages setting, which the
previous MkDocs + mike setup required.
Working on the docs locally
cd website
npm install
npm start # dev server with hot reload on http://localhost:3000
# (your edits to docs/ are under /ha-power-insight/next)
Two checks worth running before pushing, because CI runs both:
npm run typecheck # the case-diagram component and the case data
npm run build # catches broken links and broken heading anchors
The build treats broken internal links and broken anchors as errors, not warnings. That is deliberate: the reference-case pages are a specification, and a link that silently rots undermines the point.
Markdown vs MDX
markdown.format is set to detect:
.mdis parsed as plain CommonMark. Braces and angle brackets in prose and code spans are left alone, which is what most pages want..mdxis parsed as MDX and can import and render React components.
Only pages that actually embed a component — the reference-case pages, and the
landing page — need to be .mdx. Prefer .md for everything else.
Version numbers
Power Insight is versioned by the month it is released in:
YEAR.MONTH.PATCH, so the first release of September 2026 is 2026.9.0 and a
fix to it 2026.9.1. The number lives in two places that must agree —
custom_components/power_insight/manifest.json and pyproject.toml — and
uv lock then updates the lock file. The docs are cut by YEAR.MONTH (below).
Versioning
The published site carries the released versions only: the latest at the root, older ones in the navbar's version selector. The unreleased docs are never published.
| Where you edit | Where it appears |
|---|---|
docs/ | nowhere yet — locally and in pull-request builds under /next (Next (unreleased)) |
website/versioned_docs/version-2026.7/ | the site root — what visitors get by default |
The deploy build leaves docs/ out by setting DOCS_RELEASED_ONLY=true
(see website/docusaurus.config.ts). To check that locally:
cd website
DOCS_RELEASED_ONLY=true npm run build
Docusaurus versions by snapshotting source, not built output. Cutting a
version copies the whole of docs/ — markdown, the reference-case JSON, all of it —
into website/versioned_docs/, and that copy is then frozen. Day-to-day work
happens in docs/; nothing you write there changes what a visitor sees until
the next version is cut.
Cutting a version at release time
The docs-version.yaml
workflow does it. When a release is published it reduces the tag to
YEAR.MONTH (2026.11.0 → 2026.11), snapshots docs/ at the release
commit, checks that the site still builds, and opens a pull request
docs: cut docs version 2026.11. Merge that pull request and the site root
serves the new version.
| Release | What happens |
|---|---|
First release of a month (2026.11.0) | A docs version is cut and a pull request opened. |
Patch release (2026.11.1) | Nothing: 2026.11 already exists. |
| Pre-release | Nothing: its docs stay under /next until the final release. |
Before merging, check whether the release needs anything by hand, such as
links into pages that were renamed since the last version (the navbar and
footer in website/docusaurus.config.ts, and README.md).
- Settings → Actions → General → Workflow permissions → Allow GitHub Actions to create and approve pull requests, or the workflow cannot open its pull request.
- A pull request opened by a workflow does not start other workflows, so the Docs check does not run on it by itself. Close and reopen the pull request to run it.
To cut by hand (for a release published before the workflow existed, run it from Actions → Cut docs version → Run workflow with the tag), or locally:
cd website
npm run docusaurus docs:version 2026.8 # YEAR.MONTH of the release
That writes versioned_docs/version-2026.8/, a matching sidebar, and a new
entry in versions.json; the newest entry becomes what the root serves.
Commit all three.
The reference cases need no step of their own. Their results under
docs/spec/cases/ are the engine's, and CI fails any commit whose engine no
longer produces them, so cutting from a green commit freezes exactly what this
release's engine computes. uv run --group engine python tools/snapshot.py --check confirms it locally.
Old versions stay browsable and are still rebuilt on every deploy, because Docusaurus versions source rather than output — so a theme fix reaches every version at once.
That last point cuts both ways. docs/ is snapshotted; website/src/ is
shared across every version. So the reference-case JSON lives under
docs/spec/cases/ to be frozen with the prose around it, while the component
that draws it is shared — which is why each page imports its own case data and
passes it in rather than letting the component reach for it.
Break that split and an old version's page will quietly start rendering today's numbers, when its whole purpose is to show what the engine did at that version.
Older versions are not kept compatible with the component. If the case JSON
format changes, the build shows which old pages broke; remove those pages from
the old versions (and their sidebar entries) rather than teaching the component
every past format. That is what happened to 2026.7's hand-derived anchor cases,
which left a one-paragraph stub at spec/index so the navbar link keeps
working.
The MkDocs site was versioned with mike. That history stays on the old
gh-pages branch and is no longer updated or served.