Documentation and the sites

Three sites, three audiences, three publishing rules:

Site Built from Published when
docs.schellingboard.org docs/public/, at each tag a release is tagged
developers.schellingboard.org docs/dev/, on main docs/dev/ changes on main
schellingboard.org www/ + docs/screenshots/ www/ or the screenshots change

The split of audiences is the point: attendees and organizers read the first, contributors the second, and nobody should land on the wrong one. Keeping the developer docs unversioned and the user docs versioned follows from the same split — user docs must describe the release someone is running, while developer docs must describe the branch they are working on.

The public docs site

Built with docmd from docs/public/, which holds one copy of the documentation and no snapshots. Published versions are reconstructed from git: scripts/build-docs.sh walks the release tags, checks each one out into a temporary worktree, and builds it as a version. The newest is served at the site root, older ones under /<major.minor>/, all listed in the version dropdown.

So docs/public/ is the next release’s documentation. Edit it in the same commit as the change it describes; it is published when that release is tagged.

Command What it does
make docs Live preview of docs/public
make docs-build Build the published site into site/
make docs-validate Check for broken internal links (runs in CI)

.github/workflows/docs.yml deploys on tag pushes and on pushes to release/* branches. Pushing to main deliberately publishes nothing — until a release is tagged, make docs is the only place the new documentation exists.

Two consequences worth knowing:

  • Releasing documentation is tagging the repository. There is no snapshot step, so nothing can drift out of sync with the release it documents.
  • A release tag that predates the docs site publishes nothing. The build skips any tag without docs/public/index.md, which is why v3.0.0 and v3.1.0 never appear on the site.

robots.txt, sitemap.xml and 404.html are generated by docmd from the config’s url; don’t hand-write them.

Dating each page

Every page ends with “Last updated”, and the commits that touched it in a tooltip. docmd’s own git plugin cannot produce that here: it delegates to its build engine, which runs git log in the process working directory, so the worktrees the versions are built from are all “outside repository” — the error is swallowed and the site publishes undated. scripts/docmd-git-history.js (loaded from docmd.config.json as a local plugin) resolves the repository per page instead, which also means each version is dated from its own ref rather than from main. Contributor avatars are deliberately left off, so no reader’s browser is sent to Gravatar. build-docs.sh fails the build if any page comes out undated.

Hosting setup for the docs site

Two things live outside the repository, so a fresh fork or a moved domain needs them set up again before the first tag push can publish anything:

  • GitHub Pages: repository Settings → Pages → Source must be GitHub Actions. actions/deploy-pages fails without it.
  • DNS: a CNAME record for docs.schellingboard.org pointing at schellingboard.github.io. The domain itself comes from url in docmd.config.json — the build writes it to site/CNAME from there.

Until both are in place and v3.2.0 is tagged, the docs.schellingboard.org links in README.md are dead; the same pages are readable in docs/public/.

Correcting published documentation

To fix published docs without cutting a release, commit to the release branch of that version — release/3.2 for the 3.2 docs. The build prefers that branch over the tag, so pushing it republishes that version, and the deploy log names the ref each version came from (Version 3.2 ← release/3.2).

Release branches don’t exist until something needs one. Create it from the release tag the first time that minor release has to be corrected — a release that never needs a fix never gets a branch — and merge it back into main so the fix reaches the next release too. The same branch carries the commits of a patch release, so this is where a fix to an older version lives in general, not only a documentation one; see Releasing a new version.

jj new v3.2.0
jj bookmark create release/3.2   # git: git switch -c release/3.2 v3.2.0
# fix, commit, push — the docs site redeploys

Because the branch tip wins over the tag, anything committed to it is published as that version’s documentation the moment it is pushed, released or not. Land work that isn’t ready to be read on a separate branch.

The developer docs site

The site you are reading. docmd.dev.config.json builds docs/dev/ into dev-site/ with its own title, navigation and search index — a second docmd project in the same repository, sharing nothing with the user site but the theme and the logo. ADR 0008 has the reasoning, including why it isn’t a path under docs.schellingboard.org.

Command What it does
make docs-dev Live preview of docs/dev (no diagrams — see below)
make docs-dev-build Build the published site into dev-site/
make docs-dev-validate Check for broken internal links (runs in CI)

It is not versioned. .github/workflows/developers.yml publishes on pushes to main that touch docs/dev/, so the site always describes the default branch; a contributor on a release branch reads that branch’s docs/dev/ in the repository instead.

Links out of docs/dev/ must be absolute GitHub URLs. The site’s root is docs/dev/, so a relative ../../CONTRIBUTING.md resolves to nothing once published. make docs-dev-validate catches those, and is the reason to run it after moving a file. Links to user documentation go to docs.schellingboard.org rather than to docs/public/ in the repository.

docs/dev/README.md is both the site’s home page and the folder’s index on GitHub, which is what keeps the repository copy navigable.

Why it deploys to a second repository

The same reason as the landing page below: GitHub Pages serves one custom domain per repository, and this one already serves docs.schellingboard.org. The build is pushed to schellingboard/developers.schellingboard.org, which holds nothing else, using the same organization-owned deploy app the landing page uses — see Hosting setup for the pushed sites.

The architecture diagrams

The C4 model in docs/dev/target-architecture/diagrams/ reaches the site twice:

  • /diagrams/ — the full LikeC4 explorer, every view with drill-down between levels, built by likec4 build. It uses hash history (/diagrams/#/…) because GitHub Pages can’t route deep links into a single-page app.
  • <likec4-view view-id="…"> — chapters embed a single view inline, rendered by the web component likec4 gen webcomponent produces.

There are no committed PNG exports, so a diagram cannot go stale: the site renders from the .c4 sources on every build. The cost is that GitHub shows no diagram at all when reading the chapters in the repository, and that make docs-dev — plain docmd dev, with none of the LikeC4 steps — doesn’t either. Use make arch-diagrams for the explorer while editing the model, and make docs-dev-build to see an embedded view rendered.

The web component bundle is ~2.5 MB, which is why scripts/likec4-embed.js, not the bundle, is the site-wide customJs: it loads the bundle only on pages that actually contain a <likec4-view>, and mirrors docmd’s theme onto each element’s color-scheme.

build-dev-docs.sh fails if a chapter embeds a view-id that views.c4 doesn’t define. The element renders an empty box otherwise, which is the same silent staleness the PNGs had.

The landing page

schellingboard.org is hand-written HTML in www/ — a landing page and a screenshot gallery. make www copies it, plus docs/screenshots/, into www-site/; open www-site/index.html to preview. There is no generator, on purpose: the pages are two bespoke layouts, and rendering them through docmd would mean custom templates to make a docs tool stop looking like one.

.github/workflows/www.yml publishes on pushes to main that touch www/, docs/screenshots/ or the build script. It does not follow the documentation site’s release-tag rule, because the page describes the project rather than a version — with the consequence that its screenshots can show interface changes that are merged but not yet released.

Why it deploys to another repository

GitHub Pages serves one custom domain per repository and this one already serves docs.schellingboard.org, so the built site is pushed to schellingboard/schellingboard.org, which holds nothing but that output and is never edited directly. Its Pages source is the default branch, so the site keeps working even if the workflow is disabled.

Screenshots

docs/screenshots/ is the only copy of the screenshots. The landing page uses them directly and the documentation site can use them too — see docs/screenshots/README.md for the capture checklist and how to reference them from markdown.

They are not under docs/public/, and cannot usefully be: docmd discovers markdown and nothing else, so a PNG placed there is not copied to the output. Both build scripts do the copying themselves. One consequence: images appear in make docs-build output but not in the make docs live preview, which serves what docmd produced.

Hosting setup for the pushed sites

Both the landing page and the developer docs are pushed into a repository of their own, and both use the same setup.

  • GitHub App: these workflows write to a repository that is not their own, so they mint a token from an organization-owned app rather than carrying a standing credential. The token expires within the hour, the app belongs to the organization instead of a person, and it is installed on the site repositories alone.

    1. Under the organization’s Settings → Developer settings → GitHub Apps, create an app with the repository permission Contents: Read and write and nothing else. It needs no webhook and no account permissions.
    2. Install it on schellingboard/schellingboard.org and schellingboard/developers.schellingboard.org only. Each workflow still narrows its own token to the one repository it publishes, so installing the app on both does not let either job touch the other’s site.
    3. In this repository, add the app’s Client ID as the PAGES_DEPLOY_APP_CLIENT_ID variable and a generated private key as the PAGES_DEPLOY_APP_PRIVATE_KEY secret.

    To rotate, generate a new private key on the app and replace the secret; the client ID does not change.

  • DNS: schellingboard.org and developers.schellingboard.org each pointing at schellingboard.github.io, and each site repository’s Pages source set to its default branch — not GitHub Actions, which would ignore the pushed files. The domain comes from www/CNAME for the landing page, which the build copies verbatim, and from url in docmd.dev.config.json for the developer site, which build-dev-docs.sh writes to dev-site/CNAME.

  • A first commit: the workflow clones the site repository before writing to it, so an entirely empty one fails. Any initial commit does.