Skip to content

Writing documentation

The documentation is Markdown under docs/, built into a site by Zensical and published at kosmos.law/docs.

Build it locally

scripts/build-docs.sh            # build into site/
scripts/build-docs.sh --strict   # what CI runs; fails on a broken link
uvx zensical@0.0.67 serve        # live preview at http://localhost:8000

Zensical runs as a standalone tool through uvx, not as a project dependency: it needs a newer pymdown-extensions than the application pins. The version is set in scripts/build-docs.sh; use the same one for serve.

The build writes to site/, which is ignored by git. If the Django dev server is already on port 8000, pass --dev-addr localhost:8001 to serve.

How the site is published

The site is static files, and the server that hosts kosmos.law receives only those: it has no checkout of this repository for the docs and no build tools. The build happens wherever this repository already is, and scripts/publish-docs.sh copies the result across:

scripts/publish-docs.sh user@host:/www/kosmos-docs

It builds with --strict, then mirrors site/ to the target with rsync, so a page removed from the docs is removed there too. Because mirroring deletes, the script first checks that the target is new, empty or already a docs build, and refuses anything else. --dry-run shows what would change without copying. The target can also be given as DOCS_PUBLISH_TARGET.

On the receiving server, nginx serves that directory under /docs/ from the same server block as the landing page:

location /docs/ {
    alias /www/kosmos-docs/;
    index index.html;
    error_page 404 /docs/404.html;
}

scripts/add-docs-location.sh adds that block to an nginx site file: it backs the file up, inserts the block, tests the configuration and reloads nginx, and restores the backup if the test fails. It is a one-time step per server:

sudo scripts/add-docs-location.sh /etc/nginx/sites-available/<site> /www/kosmos-docs

site_url in zensical.toml is https://kosmos.law/docs/; the pages link to each other relatively, so the same build also works under any other address.

Nothing is published from GitHub. The workflow in .github/workflows/docs.yaml only checks that a pull request's docs build without a broken link.

How it looks

The site wears the landing page's design: the same typeface, the same mark and, at night, the same palette. The landing page is its own repository (Kosmos-Law/web) and has no day side, so the light scheme borrows the application's nord-light theme. The reader's system setting picks the scheme first shown, and the toggle in the header switches it.

All of it lives in docs/stylesheets/kosmos.css: a block of design tokens for each scheme, copied from those two sources, and the rules that restyle the theme on top of them. zensical.toml sets the typeface and the two schemes. When the landing page's or the application's palette changes, change the tokens at the top of that stylesheet to match.

Where a page goes

The site has one section per reader. Decide who the page is for before writing it.

Section Reader They want to
admin/ The person running a Kosmos server install, configure, connect services, upgrade, recover
guide/ Attorneys and staff get a task done in the application
dev/ Contributors and coding agents change the code safely
reference/ Anyone look up a variable, command, schedule or permission
decisions/ Contributors understand why something is the way it is

A page that serves two readers is usually two pages. Setup steps for an integration belong in admin/; how its code works belongs in dev/, and each links to the other.

Add every new page to the nav list in zensical.toml. A page left out of the navigation is easy to lose.

One kind of page at a time

Each page does one of three jobs. Mixing them is what makes documentation hard to use.

  • How-to: numbered steps toward a goal the reader already has. Start with what they need before they begin; end with how to check it worked.
  • Explanation: how a part of the system works and why. Prose, with the file paths a reader needs to find the code.
  • Reference: tables and lists to look things up in. No narrative.

House rules

  • Write for someone who was not there. Define a term the first time it appears. Do not assume the reader knows the firm, the history, or a branch name.
  • Keep examples firm-neutral. Use example.com hosts and invented people and matters. Never put a real client, matter, address, token or route id in a page. The repository is public.
  • Say only what the code does. Check each command, variable name and path against the source before committing. If the behaviour is awkward, document the behaviour and open an issue; do not describe the system you wish existed.
  • Do not hand-maintain what can be generated. Lists of environment variables, management commands and schedules belong in reference/ and come from the code.
  • Link, do not repeat. A fact lives on one page. Other pages link to it.
  • Punctuation. No em dashes: use two sentences, a colon, or parentheses.
  • Screenshots come only from an instance loaded with demo data, never from a real firm's database.

Pages moved in from before these rules existed do not all follow them yet. Fix what you touch.

User guide pages

Pages under docs/guide/ are for attorneys and staff, not for people who run servers. They follow the house rules above and these as well. Matters is the model to copy.

  • Write from the screen. Read the templates, forms and views, and name every button, field, column and menu exactly as the screen does, in bold: Matters, Add Matter, Submit. Use "→" between the steps of a menu path: Settings → Appearance. Never invent a label. An icon with no text is described by what it is ("the plus button").
  • Task first. Headings are things the reader wants to do or understand: "Open a new matter", "Close a matter". Steps are numbered, and each starts with the action. After the steps, one sentence says what the reader should now see.
  • Second person, present tense, short sentences, plain words. No "simply", "just" or "easy".
  • Nothing the reader cannot see. No code, file names, addresses with ids in them, environment variables or database terms.
  • Good to know. When the application does something a careful user would not expect, say so once, plainly, in a short "Good to know" list at the end of that section: three items at most, and only real ones. If it is a bug, fix the bug and leave the item out.
  • Permissions in one sentence. "You need the Financial permission for this. Ask your administrator."
  • Invented examples only: the matter "Rivera v. Northside Logistics", the client "Elena Rivera", the firm "Example Law".
  • Shape. An H1 title, then two or three sentences on who the page is for and what it covers. Plain Markdown: headings, numbered lists, short tables, at most one !!! note per page. Prose wrapped at about 76 columns. No screenshots yet.

Decision records

Write a record in decisions/ when a design choice would otherwise have to be rediscovered: a feature retired, an approach tried and rejected, a constraint that is not visible in the code. Give it a date, say what was decided and what the alternatives were, and add it to decisions/index.md. Records are not rewritten later. If a decision is reversed, write a new record and link the two.

Links between pages are relative (../admin/configuration.md) and are checked at build time. A link to a file outside docs/ cannot be relative, because that file is not part of the site. Use the full GitHub URL on the dev branch:

https://github.com/Kosmos-Law/kosmos/blob/dev/deploy/README.md