Add Zola-based documentation site with essays and solutions - #85
Open
alycda wants to merge 5 commits into
Open
Conversation
Publishing the repo's writing to GitHub Pages, with mdbook rejected in
favour of Zola. The deciding factor was the shape of the content: this is
a dated, tagged, growing knowledge store, not a linear manual. mdbook
models one ordered book behind a hand-maintained SUMMARY.md and has no
metadata layer at all — no frontmatter, no dates, no taxonomies — so the
"filed under" chips, the reading time, and next/previous would each have
been a preprocessor to write and keep working. Zola has all three natively.
Its default look is also nothing, which is the point: mdbook's chrome is
the clinical feel, and restyling it means fighting a theme built around
the book metaphor.
The one real cost of Zola is that it reads TOML frontmatter under +++,
and docs/solutions/ is YAML that agents already parse. Converting in
place would have been a one-way door on the sources to suit the
generator. So site/bin/collect.py translates on the way in instead:
docs/ stays plain markdown, site/content/ and site/data/ are build
artifacts, gitignored and rebuilt every time.
Details worth knowing:
- Unknown frontmatter keys pass through to [extra], so adding a field to
a solution doc needs no collector change.
- Solutions flatten. On disk they nest a level deep by category, which is
a good way to browse a repo; Zola would read that directory as a
subsection wanting its own _index.md, so the nesting is replayed as a
`category` taxonomy value instead.
- CONCEPTS.md becomes the entity index. `{{ e(name="Base profile") }}`
renders a linked [E04], and a page's `concepts:` list produces
definitions at its foot. This is the borrowed part of the design that
had to be made load-bearing: an entity reference with no glossary
behind it is decoration, and the glossary already existed.
- A concept name with no entry degrades to a visible [Name?] rather than
failing the build, so renaming one cannot break a deploy.
- Section numbering (§ 01) is a CSS counter. Sources stay plain `##`.
- No webfonts are vendored; the stack falls back to faces already on most
machines, so the site is fully styled with zero font bytes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7JLHmbox2A7tpyCU5HqvB
Both recipes and both CI steps get Zola and pyyaml from nixpkgs via `nix shell` rather than a marketplace action, so `just docs-build` and CI build with the same versions — the same reasoning as the statix/deadnix steps in nix.yml. The Pages workflow is path-filtered to docs/, CONCEPTS.md and site/. A Nix-only change is the common case in this repo and has no effect on the site, so it should not spend a deploy. PRs build without publishing, which is enough to catch a broken template or an unresolvable internal link before it reaches main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7JLHmbox2A7tpyCU5HqvB
The trap here is that site/content/ and site/data/ look like ordinary content and are not: they are wiped and regenerated on every build, so editing them is always wrong and always silently lost. Writing that down next to the collector's behaviour table, before anyone has made the mistake, since the whole point of the generated tree is that docs/ stays the thing you edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7JLHmbox2A7tpyCU5HqvB
Contributor
⊕ Entity-level changes.github/workflows/pages.yml
.gitignore
CLAUDE.md
docs/essays/_index.md
docs/essays/the-same-bug-three-times.md
docs/solutions/_index.md
flake.nix
justfile
site/bin/collect.py
site/config.toml
site/sass/main.scss
site/templates/base.html
site/templates/concepts.html
site/templates/essay.html
site/templates/index.html
site/templates/macros/bits.html
site/templates/section.html
site/templates/shortcodes/e.html
site/templates/solution.html
site/templates/taxonomy_list.html
site/templates/taxonomy_single.html
Summary: 109 added, 6 modified, 1 deleted, 1 renamed across 21 files (6 added orphans) functions and classes, not lines · sem |
CI failed to build the site: nixpkgs ships Zola 0.22.1, and the config
was written against 0.19, whose [markdown] schema it rejects outright.
The syntax error is the symptom. The cause is that `nix shell
nixpkgs#zola` resolves against the machine's flake registry rather than
this repo's flake.lock, so a local build and a CI build of the same
commit could get different Zola versions — and did. Fixing only the
config would leave that live, with the next nixpkgs bump free to break
the build again from a clean tree.
So the toolchain moves into a `docs` devShell alongside the existing
`tools` and `default` shells, and both `just` and the workflow go
through `nix develop .#docs`. A version bump can now only arrive via
flake.lock, where it is visible in a diff and lands on one commit
instead of appearing spontaneously on whichever machine updated its
registry last.
The 0.22 config changes, for the record:
- Highlighting moved from flat [markdown] keys (highlight_code,
highlight_theme, highlight_themes_css) to a [markdown.highlighting]
table taking style = "class" plus a light_theme/dark_theme pair.
Class-only output without a named theme is no longer possible.
- Theme names come from giallo now: gruvbox-light-medium, not
gruvbox-light.
- Generated stylesheets are giallo-{light,dark}.css, so base.html and
the gitignore entry follow.
Verified against 0.22.1 rather than the 0.19 I had been building with —
`pre.giallo.z-code` renders with the site's own panel background, since
`.prose pre` outspecifies giallo's `.z-code`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7JLHmbox2A7tpyCU5HqvB
alycda
marked this pull request as ready for review
August 5, 2026 08:48
alycda
pushed a commit
that referenced
this pull request
Aug 5, 2026
The skill-creator trigger evaluation (20 realistic queries x 3 runs via claude -p) showed the original description badly under-triggered: 0/10 should-trigger queries consulted the skill, while all 10 near-miss negatives correctly stayed quiet. The failing clause was the abstract one — "use whenever the question is semantic rather than textual" loses to grep/git-diff muscle memory every time. Part of the zero was harness artifact (the eval runs in an empty directory and only counts the skill if it's the FIRST tool call), so the rewrite was validated manually instead: with this description, queries run inside a real repo consult the skill as their first action and then run the availability check it teaches (4/4 previously failing queries), while the riskiest near-miss — "review PR #85 but just the commit messages" — still routes to commit-craft, not here. What changed: abstract trigger conditions became concrete task shapes ("what breaks if I rename X", "which parts of this 4000-line PR need human review", "blame wrecked by a formatting commit", "false conflicts from parallel agents in one file"), plus an explicit consult-this-FIRST framing — skills under-trigger by default, and a description has to outbid the model's habit of just using plain git. Every clause stays function/code-anchored so semver, lockfile conflicts, and commit-message review keep failing to match. Eval set and results are in the session workspace, not committed — they're a measurement, not content; re-derive with skill-creator's run_eval.py when the description next changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J2Mv7iPiENFihUPt2zNdRe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces a static documentation site built with Zola, publishing the repository's essays and solutions as a browsable website. The site is authored in plain markdown with YAML frontmatter in
docs/, then translated to Zola's format at build time via a Python collector script. The site is published to GitHub Pages via a new CI workflow.Key Changes
Site infrastructure (
site/)config.toml: Zola configuration with taxonomies for tags and categoriessass/main.scss: Complete stylesheet with warm, paper-like aesthetic and dark mode supporttemplates/: Base layout, essay/solution/section/taxonomy templates, and shared macrosbin/collect.py: Python script that translatesdocs/markdown + YAML into Zola's TOML format and rendersCONCEPTS.mdinto a data file for entity referencesDocumentation sources (
docs/)essays/_index.mdandsolutions/_index.md: Section landing pagesessays/the-same-bug-three-times.md: Example essay demonstrating the [E01]-style entity reference systemCI/CD (
.github/workflows/pages.yml)Build integration (
justfile)docs-collect: Regenerate site content from sourcesdocs-build: Full site builddocs-serve: Local development server with live reloaddocs-fonts: Instructions for optional webfont vendoringDocumentation (
CLAUDE.md)site/content/andsite/data/are build artifacts, never edited by handImplementation Details
Design philosophy: The site reads like "something a person wrote" rather than generated documentation. It uses serif fonts for prose, a single narrow column, and a warm paper-like color scheme with dark mode support.
Collector behavior: Rather than reformatting sources to suit Zola,
collect.pytranslates them on the way in:+++categorytaxonomyCONCEPTS.mdintodata/concepts.tomlfor entity reference resolutionEntity references: Essays can declare
concepts:in frontmatter to reference shared vocabulary. The{{ e(name="...") }}shortcode resolves these to[E01]-style links in the rendered output, with definitions displayed at the end of each essay.Gitignore:
site/content/,site/data/, andsite/public/are marked as build artifacts and ignored.https://claude.ai/code/session_01X7JLHmbox2A7tpyCU5HqvB