Skip to content

Commit dc55089

Browse files
committed
feat: front-matter doc index generator
- Introduce `tools/gen_doc_index.py` to derive class document tables and topic listings from document front matter, with strict validation for metadata, stub status, topic vocabulary, and optional nav/tree consistency checks for CI (`--check`, `--nav`). - Update documentation standards and tooling docs to require front matter-driven indexing and two-level Diataxis indexes. - Add pytest coverage (plus test import setup) for generation, validation failures, and navigation drift cases.
1 parent 9408caf commit dc55089

5 files changed

Lines changed: 751 additions & 5 deletions

File tree

docs/meta/documentation.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ documents written for contributors and operators, not to docstrings, which are c
55

66
In brief:
77

8-
- Organize `docs/` by [Diátaxis](https://diataxis.fr/) class, with `docs/README.md` as the index.
8+
- Organize `docs/` by [Diátaxis](https://diataxis.fr/) class, each class carrying its own index.
9+
- Declare a document's title, summary, status, and topics in front matter, and generate the indexes from it.
910
- Open every document with a summary that survives truncation.
1011
- Order headings from overview to detail so a reader can stop at any depth.
1112
- Give reference documents a code map from concept to file and symbol.
@@ -21,11 +22,43 @@ set. Change proposals are welcome where a rule is overly restrictive, missing, o
2122
## Organization
2223

2324
Documents are filed by Diátaxis class: `explanation/`, `reference/`, `how-to/`, and `tutorial/`, using only the classes
24-
a repository actually needs. `docs/README.md` indexes the tree and says what each document holds.
25+
a repository actually needs. A class directory is created when its first document is written, never in advance.
26+
27+
Indexing is two-level. Each class carries a `README.md` that says what the class is for, lists its documents, and holds
28+
any conventions specific to that class. `docs/README.md` routes between the classes and does not list individual
29+
documents, so the landing page stays a constant size as the tree grows.
2530

2631
Diátaxis settles where a document is filed and what mode it is written in. The rules below govern quality, and they
2732
apply within every class.
2833

34+
## Front matter and generated indexes
35+
36+
Every curated document declares its own metadata, and every index that lists documents is generated from those
37+
declarations. An index maintained by hand disagrees with the tree it indexes, and the disagreement is invisible until a
38+
reader is already lost.
39+
40+
```yaml
41+
---
42+
title: Job matching and pop # equals the document's level-one heading
43+
summary: One line, at most 200 characters, shown wherever this document is listed.
44+
status: authored # authored | stub
45+
topics: [requests, workers] # each defined in the topics page
46+
order: 30 # optional; sequences the document within its class
47+
---
48+
```
49+
50+
- `summary` is what an index shows. It states what the document holds, not that the document exists.
51+
- `status` is `authored` or `stub`. A stub is published rather than withheld: it scopes what the document will cover,
52+
names the code that holds the behavior until then, and is marked as planned in its class index. A stub says what is
53+
planned; it never says when.
54+
- `topics` are subject tags shared across classes. A topics page defines each tag and collects the documents carrying
55+
it, which is how a reader holding a subject reaches the explanation, the reference, and the procedure together. A tag
56+
must be defined before it is used, and a defined tag no document uses is removed. Without that rule the tag set
57+
splinters into near-duplicates and stops being a navigation surface.
58+
59+
Where a repository publishes a site, navigation order and display titles stay curated; navigation membership does not.
60+
Membership is checked against the tree so a document cannot be added without appearing, or removed while still listed.
61+
2962
## Opening summary
3063

3164
Every document opens with a summary a reader can stop after and still know what the document covers, who it is for, and
@@ -93,7 +126,9 @@ A documentation change lands in the same pull request as the behavior change it
93126
the code it documents is treated as a defect in that pull request.
94127

95128
A repository publishing its docs runs the gates in CI: the site builds in strict mode, every generated index verifies
96-
clean against its records (`--check`), license compliance passes, and internal links resolve. A generated file that can
129+
clean against its inputs (`--check`), front matter is valid and agrees with the document body, site navigation matches
130+
the tree, cited code paths exist, prose style rules that can be checked mechanically are checked, license compliance
131+
passes, and internal links resolve. Tooling that gates the documentation is itself tested. A generated file that can
97132
drift from its inputs without failing CI will.
98133

99134
## Architecture Decision Records
@@ -124,8 +159,9 @@ and links its replacement. A decision changed in part stays `accepted` and links
124159
two ways as well.
125160

126161
The directory stays flat and numbers are global: never reused, never renumbered, never sharded into subdirectories.
127-
Topical organization belongs to the index. While a repository holds few records, `docs/README.md` lists them; past
128-
that, `docs/decisions/README.md` indexes them, grouped by system and showing status.
162+
Topical organization belongs to the index. The records are indexed by a generated `docs/decisions/index.md` carrying
163+
each record's status and date, and that page is how the corpus enters navigation: records are never listed
164+
individually, so navigation does not grow with the record count.
129165

130166
An accepted record is immutable in substance. Permissible edits are typo fixes and repairing file or symbol references
131167
that the codebase has moved out from under the record. Context, Decision Outcome, and Consequences are never rewritten

tools/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,49 @@ python docs/haidra-assets/tools/gen_adr_index.py docs/decisions --check
2020
The index renders on GitHub (which draws `mermaid` fences natively) and on any static site generator whose
2121
Markdown pipeline has a Mermaid fence configured, such as Zensical via `pymdownx.superfences`.
2222

23+
## gen_doc_index.py
24+
25+
Builds the parts of a documentation tree that can be derived from the documents themselves, so an index cannot
26+
disagree with what it indexes. Each curated document carries YAML front matter:
27+
28+
```yaml
29+
---
30+
title: Job matching and pop # must equal the document's level-one heading
31+
summary: One line, at most 200 characters, shown in every index that lists this document.
32+
status: authored # authored | stub
33+
topics: [requests, workers] # each topic must be defined in topics.md
34+
order: 30 # optional, orders the document within its class (default 100)
35+
---
36+
```
37+
38+
From that, the tool rewrites two kinds of generated block, leaving the hand-written prose around them untouched:
39+
40+
- `documents` in each class index (`docs/<class>/README.md`): a table of authored documents, then a table of
41+
planned stubs.
42+
- `topics` in `docs/topics.md`: every topic with the documents that declare it, across classes.
43+
44+
A block is delimited by `<!-- BEGIN GENERATED: name (gen_doc_index.py) -->` and `<!-- END GENERATED: name -->`.
45+
A documentation class is any directory under the docs root holding a `README.md`, minus `--exclude` names
46+
(`haidra-assets` by default). Decision records are excluded by construction: they carry an `index.md`, not a
47+
`README.md`, and are indexed by `gen_adr_index.py`.
48+
49+
```sh
50+
# Rewrite the generated blocks
51+
python docs/haidra-assets/tools/gen_doc_index.py docs
52+
53+
# CI: fail on a stale block, invalid front matter, or navigation drift
54+
python docs/haidra-assets/tools/gen_doc_index.py docs --check --nav zensical.toml
55+
```
56+
57+
The tool fails rather than generating a misleading index when a document's front matter is missing or invalid,
58+
its title disagrees with its heading, its `status` disagrees with the presence of a stub banner, or it uses a
59+
topic that `topics.md` does not define (and, in reverse, when `topics.md` defines a topic no document uses).
60+
61+
`--nav` cross-checks a Zensical configuration against the tree. Navigation order and display titles stay
62+
curated; membership does not. It fails when a curated document is missing from `nav`, listed twice, or absent
63+
from disk, and when a generated record such as `decisions/0001-*.md` is listed individually instead of being
64+
reached through its index.
65+
2366
## check_code_refs.py
2467

2568
Fails when documentation cites a repository path that does not exist. It extracts backticked tokens that start

0 commit comments

Comments
 (0)