Skip to content

Spec: restructure docs into Diátaxis

  • Repository: gitlab.com/phpboyscout/cicd
  • Status: approved — reviewed and approved with Matt in-session; implemented incrementally across the MRs in D7.
  • Target: the published docs site only (docs/, built by the zensical-pages component dogfooded on this repo). README.md and CLAUDE.md get a smaller, related cleanup pass (D6) but are not restructured.
  • Driver: the repo now ships 24 components across 8 tracks, but docs/index.md is dated 2026-05-15 and only documents the original 4 v0.1 gate components — 19 of 24 components have zero page on the live site. docs/development/specs/ dumps 31 dated decision records flat into the nav with no curation. README.md, by contrast, is current (accurate through v0.19) — the gap is specifically the published site, because content has lived in two places (README prose vs. an unmaintained site) with no single source of truth and no workflow step that touches the docs site when a component ships. Matt has adopted Diátaxis as the documentation methodology across his projects; this is the first project to apply it.

Decisions

D1 — Four quadrants + a contributing section, one component-reference page per template

docs/ gets four top-level Diátaxis directories — tutorials/, how-to/, reference/, explanation/ — plus a contributing/ section for the contributor-facing material (component-authoring.md's content, refreshed, plus a decision-log index). docs/development/specs/ keeps its path unchanged (D3), and — since the D2 second revision — development/ is itself a fifth, nav-visible top-level section: an explicitly approved concession outside the strict four-quadrant model, for raw decision records and any repo-internal developer guides.

reference/ gets one page per component (24 pages, 1:1 with templates/*.yml), grouped into subdirectories by track (tofu/, go/, rust/, svelte/, pages/, automation/, skills/) for nav sectioning — not grouped multi-component pages. A 1:1 mapping is predictable, addressable, and trivially auditable: "does every templates/<name>.yml have a matching reference/**/<name>.md?" is a simple diff of two file-name lists, which is also the completeness check in D5.

Each reference page mines primarily from README.md (already accurate) cross-checked against the component's spec.inputs, in a consistent shape: purpose, full inputs table (name/type/default/description), jobs produced, a minimal usage example, links to the explanation page(s) and spec(s) that justify its design.

D2 — Zensical nav: auto-generated, no explicit array (revised twice — see Resolved)

This section has been revised twice. Drafted as "auto-nav, no explicit nav: array" → reversed to an explicit [project] nav = [...] array after the first build showed specs leaking into the nav regardless of hide: [navigation] → reversed back to auto-nav, on explicit direction from Matt, once the explicit array itself proved to be the wrong trade-off (curation overhead; new pages silently invisible until someone remembers to add them to the array). See Resolved below for both corrections; this section states the mechanism actually shipped.

zensical.toml carries no nav key at all. Zensical's Python config layer (config.py) auto-generates nav from the docs/ directory tree whenever nav is omitted — every directory with an index.md becomes a nav section, every page in it a nav entry, with zero manual upkeep as pages are added. The trade-off accepted knowingly: navigation.tabs sorts top-level tabs alphabetically under auto-nav (Contributing, Development, Explanation, How-to, Reference, Tutorials) rather than the conventional Diátaxis reading order — Matt's stated preference is auto-surfacing over precise curation, so this is accepted rather than worked around with e.g. numeric directory prefixes (which would just be hardcoding by another name).

The one place a flat auto-generated list is genuinely bad UX — 32+ dated files under development/specs/ permanently expanded in every page's sidebar — is solved client-side, not by excluding pages from the nav data model: docs/javascripts/nav-specs.js (wired via extra_javascript) collapses the individual spec list unless the visitor is actually browsing a spec; the "Specs" section header stays visible and clickable throughout. Mirrors the pattern used by go-tool-base's own dated-specs archive.

D3 — Specs stay at their current path; auto-surfaced, not excluded

docs/development/specs/ keeps its existing path — moving it would break every inbound link (README.md, CLAUDE.md, contributing/index.md, and every historical MR description that cites a spec path). Unlike the initial revision (D2), specs are not excluded from the nav in any form — they're fully auto-surfaced, same as any other page, with the sidebar-clutter problem handled by D2's JS collapse instead. The hide: [navigation] frontmatter previously added to every spec file (to suppress each spec's own sidebar-when-viewed) has been removed — a spec page now shows its normal sidebar like any other page, so a reader can navigate to sibling specs while reading one. development/specs/index.md (new) is the section's landing page and the specs-authoring guide; the curated contributing/decision-log.md (D4) remains the one-page, at-a- glance summary table.

D4 — Explanation pages are curated from spec clusters; a decision-log table is the exhaustive index

The 31 specs cluster into roughly ten Explanation-worthy themes (spec-first development; the token-input convention; OpenTofu state & caching; the dev-tools image; change-detection; security-always-on; release automation — Go/goreleaser, Rust/release-plz, Terraform modules, releaser-pleaser, as subsections of one page; Renovate automation; static sites — zensical-pages + hugo-pages; the Svelte frontend track, including the go:generate-vs-svelte-build "why"). Roughly seven specs are narrow patches with no broader design lesson (a retry-count bump, an image-default bump, a job-ordering fix, …) — these get a row in contributing/decision-log.md (a table: spec, status, one-line summary, link) rather than a dedicated page. The decision-log table is exhaustive (covers all 31+ specs); explanation pages are curated and link back to the specs that justify them.

D5 — The keep-fresh guardrail: a docs step in the contributor workflow

The 19-component gap happened because no step in the existing "add a component" workflow (spec → template → self-test → CHANGELOG → release) touched the docs site. contributing/index.md (replacing component-authoring.md) adds a step: add/update docs/reference/<track>/<name>.md alongside the existing steps. This is the structural fix, not just the one-time backfill.

README.md is accurate today but duplicates what reference/ will now own authoritatively, recreating the dual-source-of-truth problem that caused the original gap. Right-size it to a thin pointer + quickstart that links into the live docs site, once the site has the content to link to (D7 step 6). Two stale claims get corrected in the same pass: README's "Catalog publishing lands in v0.2" claim (verify current status, since we're at v0.19 and it still doesn't appear wired — correct whichever way the facts land, don't just delete the claim) and component-authoring.md's workflow step referencing a develop-branch merge flow that doesn't exist (the repo is trunk-based; MRs target main directly, releaser-pleaser drives releases). CLAUDE.md's layout tree and component list get refreshed to match the current 24-component, dev-tools-image, change-detection reality.

D7 — Rollout: six small MRs, sequenced for value-first delivery

  1. This spec.
  2. Skeleton + reference — the four quadrant dirs + contributing/, all 24 reference pages + section index pages, hide: [navigation] on every spec. Nav verified against a real Zensical build using the pinned zensical==0.0.33 (both the root zensical-build/pages job that dogfoods this exact docs build, and a local build for fast iteration) — this is where D2's auto-nav assumption was found wrong and corrected to an explicit nav: array; tab order and full in-section sidebars confirmed against the corrected mechanism. Highest value, most mechanical — closes the 19-component gap on its own even if nothing else lands.
  3. Explanation — the ~10 pages from D4.
  4. How-to guides — six task-oriented pages: pin/upgrade a component, add change-detection to a gate, set up OIDC for tofu-plan/apply, publish a release (releaser-pleaser vs. release-plz vs. tofu-module-publish), embed a Svelte frontend (the go:generate-vs-svelte-build decision + wiring), deploy a docs site (zensical-pages/hugo-pages, dogfooding this repo).
  5. Tutorial — one flagship walkthrough, your-first-go-pipeline.md (zero → green pipeline on the Go track: go-lint/go-test/go-security/ goreleaser). Written last so it can link into reference pages that already exist. Rust/Terraform/Svelte tutorials are an acknowledged backlog noted in tutorials/index.md, not built in this pass — writing four high-quality tested tutorials in one pass is more than this rollout needs to land the structural fix.
  6. Cleanup — D6.

Each step is its own MR, watched to green and handed off per the existing release rhythm — small, reviewable diffs rather than one large docs MR.

D8 — Versioning

Pure documentation — no component input or behaviour changes. docs: commits, no release / no version bump, matching how the rest of the repo treats docs-only changes (see e99b7fa, bf5a15d).

Resolved (review)

  • Reverted to auto-nav, reversing the explicit nav: array from the first revision (2026-07-02, on Matt's explicit direction) — the explicit array (below) solved the specs-leaking-into-nav problem, but introduced a worse trade-off: curation overhead (every new page needs a matching manual edit or it silently never surfaces) and important docs — specifically the specs themselves — no longer surfacing automatically, which runs directly against the reason a spec-first repo keeps its decision record in the first place. Reverted zensical.toml to no explicit nav key at all (D2), removed hide: [navigation] from all 32 specs (D3), and solved the original clutter problem with a client-side script (docs/javascripts/nav-specs.js, modelled on go-tool-base's equivalent) that collapses the specs list in the sidebar rather than excluding it from the nav — auto-surfacing and a tidy sidebar are not actually in tension once the collapse is client-side rather than nav-data-model-level. Added docs/development/index.md and docs/development/specs/index.md (the latter also documents the spec-writing process, mirroring go-tool-base's equivalent page) so development/ reads as a deliberate top-level section rather than an accidental one. Accepted consequence: alphabetical tab order, since tab order is only controllable via an explicit array — simplicity and auto-surfacing were the stated priority over precise curation.
  • Explicit Zensical nav: array, reversing the initial "no explicit nav" draft (2026-06-30, during D7 step 2 implementation) — the original D2 draft, based on source-level research into Zensical's nav generation plus the keyrx precedent, concluded auto-nav from the directory tree was sufficient and that per-page hide: [navigation] frontmatter would keep specs out of the primary nav. Building the actual site (pinned zensical==0.0.33, both locally and via the real zensical-build job) proved both assumptions wrong: auto-nav pulled in all 32 spec pages as a nested "Development" tab regardless of their hide: [navigation] frontmatter (that key only suppresses a page's own sidebar when viewing it — it does not exclude the page from other pages' nav trees), and delivered alphabetical tab order (Contributing, Development, Explanation, How-to, Reference, Tutorials) rather than the intended Diátaxis reading order. Switched to an explicit [project] nav array in zensical.toml (D2) — confirmed empirically to (a) exclude any page not listed from the nav tree everywhere on the site while leaving it built and directly linkable, (b) preserve full in-section sidebar navigation for every listed page, and © control tab order by list order. This is the kind of "a decision proved wrong in practice gets a dated revision note, not a silent edit" case spec-first-development.md describes.
  • Specs stay at their current path, excluded from the nav array rather than moved (2026-06-30) — avoids breaking every inbound link for a cosmetic nav-tidiness gain (D3).
  • One reference page per component, not grouped per-track pages (2026-06-30) — predictability and auditability outweigh the larger page count (D1).
  • Tutorials scoped to one flagship (Go) for this rollout (2026-06-30) — sized to land the structural fix without over-extending into four full tested tutorials up front (D7 step 5).

No open questions remain — this spec formalizes a plan already reviewed and approved with Matt in-session. D2/D3's nav mechanism was corrected twice after the plan itself was approved: once against the real build during initial implementation, and once more on Matt's explicit post-ship feedback that the resulting explicit-array design didn't match his actual priorities — see the two dated notes above.