ADR-047: A declared family replaces the default; settings still merge
Status Active · Filed 2026-08-11 · Influenced by ADR-006 · ADR-045 · Influenced ADR-048
Context
ADR-006 made document families configuration, and
ADR-045’s executable examples measured how far that promise
actually reached. Two of the limits they pinned shared one root cause:
configuration merged over DEFAULTS with a single per-key rule, everywhere.
- The shipped
ADRscheme could not be removed. A record of RFCs and specs still carried an ADR scheme pointing atrecord/decisions.dand still rendered an empty decision index. - A declared scheme’s omitted key inherited the default entry’s value.
[luria.schemes.ADR] dir = "decisions"with nooutput— the natural way to adopt without moving files — inheritedoutput = "docs/decisions"and silently relocated the index.
Both were documented in the configuration reference under “What is not
configurable”, asserted by tests, and worked around in
examples/collocated/ with an output = "decisions" line whose only job
was to defeat the inheritance. A limit that has to be that well-documented
is a bug with good paperwork.
Decision
Two merge rules, split by what a table is.
Settings tables merge per key: paths, code, lint, site, and the
top-level scalars. Their keys are Luria’s vocabulary; a project setting one
of them is overriding, and clearing the others would turn every partial
override into a broken config.
Family tables replace whole when declared: schemes, fragments,
journals, remotes, listed in config.FAMILIES. Their entries are the
project’s vocabulary — the table’s keys are names the project chose — so
declaring the family is authorship, not adjustment. Declared, the family is
exactly what was written; undeclared, the shipped default stands.
The consequence that pays for the change: omission is meaningful again
inside a declared family. An output left out is genuinely unset, because
no default entry survives to be inherited from. The Scheme.output
docstring said omission collocates the view; it is now true without an
asterisk.
Alternatives considered
- A removal sentinel —
ADR = false, orenabled = falseper entry. Works, and invents a second vocabulary whose only purpose is to un-say what the merge rule said. A config format where absence means “default”, presence means “mine”, andfalsemeans “actually absent” has three states for a two-state question. - Replace on any subtable write. One rule instead of two, but the wrong
one for settings:
[luria.site.theme.light]would clear the rest of the site table, and every partial override becomes a trap. The two-rule split is the finding, not a complication of it. - Status quo. Two permanent warning labels in a generated reference, a workaround line in an example, and every RFC-only adopter rendering a decision index for a scheme they never asked for.
Consequences
The two pinned limits inverted, and their tests fired when the rule changed
— which is what the pins were for. examples/rfcs-and-specs now has no ADR
scheme at all, examples/collocated collocates by omission with the
workaround line retired, and examples/many-journals demonstrates the other
half: an undeclared family keeps its defaults.
The migration cost is real and narrow: a project that declared part of a
family expecting the rest from the defaults — [luria.schemes.DP] alone,
counting on ADR to persist — now owns the family it declared and must
write the missing entry. The shipped template and this repository both
declare their families in full, so the shape luria init has ever produced
is unaffected; the changelog carries the upgrade note.
doc_refs.adr_paths() now answers {} for a project with no ADR scheme,
and the index-stub fallback names the scheme it renders — both hardened by
the same examples that found the limits.