ADR-059: The configuration reference renders where its schema lives; every project gets its own record description

Status Active · Filed 2026-08-22

Context

docs/configuration.md is generated from dataclasses.fields() (ADR-044), for a good reason: a hand-written copy of a schema drifts, and this one has ~60 keys across nine tables. That reasoning is sound and is not what changed.

What changed is where the page lands. It renders into <docs>/, and <docs>/ belongs to whichever project is running the command — so every adopter of the package acquired a 336-line description of Luria’s configuration schema, committed into their documentation tree, carrying a stamp that reads:

Generated by luria index from the dataclasses in luria/config.py — built, not edited.

A contributor to that project has no luria/config.py. The instruction names a file outside their repository, in a package they installed. And the page is only as current as their pin: it describes the schema of the version they have, in a file they will not think to regenerate, with nothing in their tree responsible for noticing.

The reason this is worth a decision rather than a patch is that the page was never wrong about anything — it is accurate, generated, and lint-gated. It is simply not about the repository it is in, which is a property no amount of correctness fixes.

The half that is about their repository was missing entirely. Luria’s capability surface is luria.toml: schemes, fragment directories, journals and remotes are families a project names, and “no code path spells ADR” is the package’s own framing. But nothing rendered which families this project named, where its entries go, or what to type to add one. A newcomer’s first question — “where does my file go?” — had no generated answer anywhere, while the question they did not ask had a very thorough one.

Decision

The reference renders where its source lives. Config.owns_schema is true when <root>/luria/config.py is the very module executing, and luria index renders config_doc only then. The test is exact rather than heuristic, and it holds for the one other case where the page’s stamp is honest: a project that vendored the package instead of installing it has that file, and may edit it.

Every project gets docs/record.md, generated from current() — the loaded config, not the schema. Sections: the schemes with the shape of a code and where documents are filed and rendered; the journals; the fragment directories; the remotes it can cite; the luria new command for each kind; and the settings it moved off the defaults. It renders in Luria’s tree too, because Luria’s record is a record.

Three properties are load-bearing.

The filing table comes from new.kinds() — the mapping the CLI dispatches on — so the page cannot advertise a kind luria new would reject. A second list would be the drift this page exists not to have.

The settings section is a diff, not a dump. A reader looking at every key cannot tell which rows were decided and which merely happened. Family tables are excluded: they are the sections above, and a declared family replaces the default whole (ADR-047), so “differs from the default” is not a question that means anything about one. Nested tables collapse to a single row — a theme is one choice with two dozen colours in it, and flattening it buries every other row.

The page is a function of the config, never of the filesystem. The first draft asked Path.is_dir() to decide a trailing slash, which made it a function of the disk: a directory luria index creates during its own run answers differently before and after, so the page rendered, was written, and then compared unequal to itself. luria index && luria lint on a fresh luria init caught it. Every path’s kind is knowable from the config, and now comes from there.

The orphan is removed, once, and only when we wrote it. config_doc.retire() deletes a leftover configuration.md on the first luria index after upgrade — but only if the file still carries the generator’s marker. Deleting a file in somebody else’s repository wants a better reason than “we stopped writing it”; the marker is proof of authorship, and its absence means the path holds the project’s own prose and is left alone.

Alternatives considered

  • A config flag — [luria.docs] configuration_reference = true. The obvious move, and it fails on defaults. Off by default and Luria’s own repo needs a line of config to publish its own reference; on by default and every adopter keeps the vendored copy until they find a key they have no reason to look for. A dial whose correct setting is determined by a fact the program can check is a question that should not have been asked.
  • Render both pages everywhere. Cheapest diff, and it keeps the whole problem: the vendored schema copy is still there, still stale on the next bump, now beside a page that makes its redundancy obvious.
  • Drop the reference entirely and fold what matters into record.md. Tempting — one page, no gate. But Luria’s own readers genuinely need the key-by-key schema, and an adopting project needs it too; it just needs it at a URL, not vendored. record.md links it and does not restate it.
  • Detect by package name or an installed-vs-editable check. Both are proxies for the question actually being asked, which is “is the schema’s source in this tree?” — and that question has a direct answer.
  • Leave the orphaned copy for projects to delete. It reads as current, which is the one thing it is not. A stale document is worse than an absent one, because absence is visible.
  • Status quo. Every adopter keeps a vendored copy of a foreign schema in their docs, and still has no generated answer to “where does my file go?“.

Consequences

  • An adopting project’s next luria index deletes docs/configuration.md and writes docs/record.md. Both are reported on stdout, and the docs-index check will ask for a record.md row in their docs/README.md — a judgement call (they describe their own page), which is the right thing for a lint to ask for rather than guess.
  • The reference’s inbound links from Luria’s own docs are unchanged; nothing moved here.
  • record.md is registered in Config.is_generated, so the bare-reference lint and luria link --fix skip it — necessary, because the page is made of example codes (RFC-001) that name nothing.
  • The tests declare shapes Luria does not ship — an RFC scheme, two journals, a news.d fragment directory — so anything hardcoded to ADR/devlog fails them. Asserting against this repo’s own config would have proved only that the page matches the fixture it was written against.
  • A project that vendored the package still renders the reference, which is correct and is also the case nobody will exercise. It is asserted in tests rather than left to be discovered.