ADR-044: The configuration reference is generated from the config schema
Status Active · Filed 2026-08-11 · Influenced by ADR-006 · ADR-021 · Influenced ADR-045
Context
Luria’s capability surface is luria.toml. A scheme, a fragment directory, a
journal, a remote and the site are five composable primitives, and the record
this package keeps for itself — decisions, principles, a changelog, a devlog —
is one instantiation of them. ADR-006 decided that in the abstract, and the
code has been honest to it ever since: luria new’s kinds derive from the
config, Scheme carries a prefix, and the directive vocabulary says
inactive-ok rather than adr-ok precisely so a second scheme costs a table
and nothing else.
None of that reached a reader. The configurable surface was documented in three places, all of them arrived at too late:
config.py’s docstrings, which are in practice the configuration reference — every dataclass carries prose plus a worked TOML example.- The decision record — ADR-006, ADR-024, ADR-028, ADR-036. This is the archaeology layer: it answers why, and a reader consults it after already knowing the thing exists.
template/luria.toml’s comments, which are good, and which a project sees afterluria init— that is, after the adoption decision is made.
The read surface had no reference layer at all: docs/ contained no
configuration page and no mention of luria.toml outside generated ADR
summaries. The predictable result is that the shipped four layers read as
Luria’s parts. That is a documentation failure with a design cost, because it
suppresses the question — “could this hold RFCs, a meeting log, arXiv
citations?” — whose answer has been yes the whole time.
Decision
luria index renders docs/configuration.md, from the schema rather than
from prose kept in step by hand.
Two derivations, both mechanical. Section prose comes from
inspect.getdoc() on each config dataclass, so those docstrings stay the
single source and their indented TOML examples are re-fenced for a page. The
key tables come from dataclasses.fields().
The key tables are read, never listed. This is the invariant a future
change must not break. A field added to Site appears in the reference on the
next build, with its type and default, because nothing anywhere holds a list
of keys that someone could forget to extend. Where classification is
hand-held — which keys Luria fills from the table’s own name, which derive
from issue_url — it is presentation only, and a field missing from those
sets still gets a row, merely labelled *required*. Being wrong in that
direction produces a row with a poor label; being wrong in the other direction
produces a key nobody can discover, which is the failure this exists to
prevent.
The page registers in Config.is_generated, which does double duty:
luria index --check and luria lint cover its staleness like any other
view, and doc_refs.doc_files filters on the same flag, so a page built out
of example codes (ADR-012, RFC-7, LU-ADR-013) is skipped by the
bare-reference lint and left alone by luria link --fix. The status reports
established that pattern; this follows it.
The prose that frames the page lives in the module, not in a per-project stub — see the summary’s rejection of the alternative.
Alternatives considered
- Write the page by hand. The obvious one, and the reason the page did not already exist. It is the second copy of a schema that changes — the hand-maintained projection DP-3 names — and it would drift in the worst direction: silently, and specifically for newly added keys, which are the ones a reader most needs documented. A doc that is right today is exactly what a hand-written one looks like.
- Leave it in the docstrings. They are already good, which makes this tempting. But they are not browsable, not linkable from a badge or an index, invisible to the published site, and reaching them requires knowing which module to open — the precise knowledge a newcomer lacks.
- A per-project
README.stubfor the prose. Consistent with how schemes work, and wrong here: a scheme’s stub exists because a project’s decision index needs its own preamble. The configuration reference is Luria describing Luria’s schema, identical in every repository that installs it. - A general autodoc dependency. Heavyweight, and it renders module layout when ADR-030 says the surface is the workflows.
- Status quo. Costs the question nobody asks. Every adopter who wanted RFCs, a second journal, or linted arXiv citations had to read source to find out the tool already did it.
Consequences
The docs gain a reference layer, and docs/README.md gains its entry — which
luria lint’s docs-index check demanded immediately, unprompted, on the first
run after the page appeared.
What this obliges: the config dataclass docstrings are now published, not
internal notes. Rewording one changes a page a reader browses, and a change to
either the docstrings or the fields makes the committed view stale — so
luria index is now part of changing config.py, not only part of changing
the record. That is the intended coupling; it is also a new way to see a red
build, and worth knowing before it surprises someone.
Both guards were fired once before being trusted (DP-6), and the devlog entry
for this work records both. A hand-edit to the page makes luria index --check exit 1 and name it. A throwaway field added to Site appeared as a
documented row without the generator being touched, and made the committed
page stale — which is the whole design demonstrated in one command.
What this does not fix: adding a scheme costs one table, but renaming one, or moving its documents, is still a manual pass. The reference says so under “What is not configurable” rather than leaving a reader to discover it, and ADR-040 is the decision that would give it a command.