Configuration
Generated by luria index from the dataclasses in luria/config.py — built, not edited. A key that exists in the schema is a row here whether or not anyone remembered to describe it.
Everything Luria does is driven by one file at the project root,
luria.toml. Every key has a default, so a project with the conventional
layout needs only issue_url — and Luria runs without the file at all, on
defaults alone, which is what lets luria init bootstrap into an empty
repository.
The shape of the file
The tables below are the whole schema. Four of them — schemes, fragments,
journals and remotes — are families: you name the entries, and the name
you choose becomes part of the vocabulary. [luria.schemes.RFC] is how a
project gets RFCs; [luria.journals.incidents] is how it gets a second
journal. No code path spells ADR.
That is worth stating plainly, because this package ships one instantiation of its own machinery — decisions, principles, a changelog and a devlog — and it is easy to read those four as Luria’s parts. They are its defaults. A record made of RFCs, specs and a meeting log is the same engine with different tables.
Two merge rules follow from that split (ADR-047). A settings table —
paths, code, lint, site — merges per key: setting docs does not
clear reports. A family table is replaced whole the moment you declare
it: a project that writes [luria.schemes.RFC] and nothing else has
exactly one scheme, and the shipped ADR is simply absent. Declare a family
and it is yours entirely; leave it undeclared and the default stands. This is
also what makes omission meaningful inside a declared family — an output
you leave out is genuinely unset, because there is no default entry left for
it to inherit from.
| table | what it configures | families |
|---|---|---|
[luria] | issue links, staleness horizon | — |
[luria.paths] | where the read and write surfaces live | — |
[luria.schemes.X] | a referable document family: ADR, RFC, SPEC | yes, keyed by prefix |
[luria.fragments."dir"] | many small files assembled into one document | yes, keyed by directory |
[luria.journals.X] | dated entries that persist, rendered into books | yes, keyed by name |
[luria.remotes.X] | another project’s codes, cited from this one | yes, keyed by prefix |
[luria.code] | which source files are scanned for stale references | — |
[luria.lint] | which warning classes fail the build | — |
[luria.site] | publishing the record as a browsable site | — |
Top level — [luria]
| key | default | what it does |
|---|---|---|
issue_url | unset | A template for issue links, with {n} for the number. Writing it also tells Luria which GitHub repository this is, which is where every [luria.site] default comes from. Unset, issue numbers stay bare rather than linking somewhere wrong. |
stale_days | 90 | How long a document may sit undecided before the pending-decisions report calls it out. |
Paths — [luria.paths]
The read/write boundary: docs/ is where a reader browses, record/ is where a contributor files.
| key | default | what it does |
|---|---|---|
docs | "docs" | The read surface — prose plus every generated view. |
decisions | "record/decisions.d" | The default scheme’s source directory. Kept as its own key for projects that predate [luria.schemes.X]. |
design_principles | "docs/design-principles.md" | Where the principles document renders. |
reports | "docs/reports" | Where the status reports land. Committed views rather than build artifacts, so a README badge has a real page to point at. |
Code scanning — [luria.code]
Which files outside the record are checked for references to retired documents. A decision number in a code comment is the strongest form of the claim being checked — it is the stated reason the code is shaped that way.
| key | default | what it does |
|---|---|---|
globs | unset | Source files to scan, as glob patterns. |
historical | ['CHANGELOG.md'] | Files that are true about the day they were written and never updated — scanning them produces permanent, unactionable noise. Journals are covered automatically. |
Enforcement — [luria.lint]
The dial between reported and enforced. Status findings are warnings by default; a class named here fails luria lint instead. Acknowledgement directives keep working under enforcement, because only unacknowledged rows ever reach a class.
| key | default | what it does |
|---|---|---|
fail_on | unset | Warning classes promoted to failures. |
narrow_terms | unset | This project’s own concrete nouns. A title in a scheme marked titles_generalize that names one is reported as narrow-titles. Luria ships none — the words are yours, and empty means the class never fires. |
Schemes — [luria.schemes.X]
One entry per referable document family. The key is the prefix codes carry, so [luria.schemes.RFC] makes RFC-7 a first-class reference: luria link --fix writes its link, luria lint demands one, and luria new rfc scaffolds the next free number — none of which needs anything beyond the table.
A family of referable documents — ADR-012, RFC-7, SPEC-3.
Luria ships with one, and knowing that one is not built in is the point:
the annotation vocabulary says inactive-ok, not adr-ok, and a code
carries its prefix, so a second scheme is an entry here (ADR-006).
| key | type | default |
|---|---|---|
prefix | str | the table’s own name |
dir | Path | required |
active | str | "Active" |
render | str | "index" |
output | Path | None | unset |
allocate | str | "filing" |
titles_generalize | bool | False |
requires | tuple[str, ...] | unset |
tag_groups | tuple[TagGroup, ...] | unset |
tags_file | Path | None | unset |
field_groups | tuple[FieldGroup, ...] | unset |
references | tuple[Reference, ...] | unset |
vocabularies | tuple[Vocabulary, ...] | unset |
uniform_ok | str | None | unset |
Fragment directories — [luria.fragments."dir"]
One entry per directory whose files are assembled into a single document and then consumed. The changelog is the shipped instance; the mechanism is not changelog-shaped.
One fragment directory: where its pieces assemble to, and in what shape.
[luria.fragments]
"record/changelog.d" = "CHANGELOG.md" # the append style
[luria.fragments."record/changelog.d"] # or, spelled as a table:
file = "CHANGELOG.md"
style = "changelog"append is the narrative shape: bodies oldest-first, inserted before the
marker, so the marker stays at the end and the log reads top-down.
changelog is the release shape: each collection is one dated batch,
inserted right after the marker so the newest batch reads first, fragments
newest-first within it. The shape is configuration because the fragment
convention is the contract, not the collector (ADR-028) — the same
directory-of-fragments serves either reading order.
| key | type | default |
|---|---|---|
target | Path | required |
style | str | "append" |
Journals — [luria.journals.X]
One entry per stream of dated entries that persist. This is a family like any other — a project can run a devlog, a meeting log and an incident log side by side, each with its own granularity and output.
Dated entries that persist, rendered into books (ADR-020).
[luria.journals.devlog]
dir = "devlog.d" # entries, partitioned yyyy/mm/dd/
output = "docs/devlog" # a directory of books plus an index
granularity = "month" # year | month | day
title = "Development log"
blurb = "…" # optional prose for the indexThe difference from a fragment directory is that nothing is consumed: an entry was true when written and stays true, so the view is generated from sources that persist rather than collected from sources that are deleted.
| key | type | default |
|---|---|---|
name | str | the table’s own name |
dir | Path | required |
output | Path | required |
granularity | str | "month" |
title | str | "Journal" |
blurb | str | unset |
Remotes — [luria.remotes.X]
One entry per foreign project whose codes this record cites. The uid key is the general case: with a regex and a URL template, a remote need not hold a Luria-shaped record — or any record — at all, which is how arXiv identifiers, ticket keys and CVE numbers become linted references.
Another project’s record, cited from this one.
A reference to it composes the remote’s prefix with the foreign scheme’s
own code — LU-ADR-013 — so the namespace is explicit at the point of use
and nothing has to guess which project an unprefixed code meant (ADR-016).
[luria.remotes.LU]
name = "luria"
repo = "dmarx/luria" # GitHub owner/name
ref = "main" # branch or tag the links point at
dir = "record/decisions.d" # where its decisions live
url = "https://…/{code}.md" # optional: overrides constructionEverything but repo (or url) has a default, because the defaults are
Luria’s own conventions — a remote that uses them needs one line. A code
family with a different shape gets a schemes entry (RemoteScheme),
which wins over these remote-level settings for its own prefix.
A remote need not hold a Luria-shaped record at all (ADR-024). Give it a
uid pattern and its references are the prefix, the delimiter and
whatever the pattern matches — an arxiv id, a ticket key — constructed
through the url template, which can index the uid’s capture groups by
position:
[luria.remotes.ARXIV]
uid = "(\d{4})[.:](\d{4,5})"
url = "https://arxiv.org/abs/{1}.{2}" # {0} or {uid} is the whole tailA pin_url template names where the remote’s stable bytes live, which
is what lets luria remotes --pin endorse content that has no GitHub
file behind it (#135) — arXiv’s e-print archive is the paper where its
abstract page is a rendering:
pin_url = "https://arxiv.org/e-print/{1}.{2}"Both are the short names of a general table: a code relates to a SET of
named URIs, each through a template over one vocabulary — {code},
{number}, {prefix}, {repo}, {ref}, {dir}, {document}, {anchor}, and
{filename}, which the discovered lockfile map fills. url is
uris.read, pin_url is uris.bytes, and a relation Luria does not
ship yet is one more name:
[luria.remotes.LU.uris]
bytes = "https://gitlab.example/{repo}/-/raw/{ref}/{dir}/{filename}"
history = "https://github.com/{repo}/commits/{ref}/{dir}/{filename}"GitHub’s blob/raw pair is simply the shipped default pair of read and
bytes templates for a remote with a repo — a different forge is a
different pair of lines, not a different subsystem.
| key | type | default |
|---|---|---|
prefix | str | the table’s own name |
repo | str | unset |
ref | str | "main" |
dir | str | "record/decisions.d" |
name | str | unset |
url | str | unset |
delim | str | "-" |
uid | str | unset |
pin_url | str | unset |
pin | bool | False |
uris | dict[str, str] | unset |
schemes | dict[str, RemoteScheme] | unset |
Per-scheme remote construction — [luria.remotes.X.schemes.Y]
Optional, and only for a remote whose code families do not all construct the same way — a file per code in one, sections of a single page in another.
How one of a remote’s code families constructs (ADR-023).
A remote is not one directory of files — it is a project, and different schemes in it have different shapes. Each entry names one construction:
[luria.remotes.SG.schemes.ADR]
dir = "docs/decisions" # file per code[luria.remotes.SG.schemes.DP]
document = "docs/design-principles.md" # sections of one file…
anchor = "dp-{number}" # …at Luria's stable anchorsanchor defaults to the prefix lowercased plus the number — dp-18 —
which is the anchor shape Luria’s own document render emits, so a remote
on current conventions needs only the document line. A url template
overrides both, and a uris table names further relations for this
family alone — both with the full template vocabulary (see Remote).
| key | type | default |
|---|---|---|
prefix | str | the table’s own name |
dir | str | unset |
document | str | unset |
anchor | str | unset |
url | str | unset |
pin_url | str | unset |
pin | bool | False |
uris | dict[str, str] | unset |
The site — [luria.site]
Publishing the record as a browsable vault. Every key derives from issue_url for a GitHub project, so the conventional case needs no table at all.
How the record publishes as a browsable site (ADR-042).
[luria.site]
title = "Luria"
base_url = "dmarx.github.io/luria"
source_url = "https://github.com/dmarx/luria/blob/HEAD"
exclude = ["template/**"]Only exclude is genuinely per-project: the rest default off issue_url,
because a project that told Luria where its issues live has already told
it which GitHub repository it is (DP-3 — derive the projection).
source_url is where a link lands when its target is a repository file
the site does not publish: a workflow, a template, the licence. Empty
means “leave those links alone”, and luria site says how many it left.
The branding keys are the project’s own artwork, cited by path:
icon = "assets/brand/icon.svg" # favicon, any square image
logo = "assets/brand/lockup.svg" # shown in place of the title
logo_dark = "assets/brand/lockup-inverted.svg" # optional[luria.site.theme.light]
light = "#f4f1e8" # any of Quartz's colour nameslogo_dark is only needed when the artwork can’t invert itself. A logo
whose SVG exposes a --luria-ink custom property — the convention this
project’s own kit uses — is re-inked to the theme automatically, and one
that doesn’t is used as it is in both modes.
| key | type | default |
|---|---|---|
title | str | derived from issue_url |
base_url | str | derived from issue_url |
source_url | str | derived from issue_url |
exclude | tuple[str, ...] | unset |
icon | Path | None | unset |
logo | Path | None | unset |
logo_dark | Path | None | unset |
theme | dict | unset |
Environment variables
Two, both escape hatches rather than configuration — they change how a run behaves, never what the record is, which is why they are not in the file.
| variable | effect |
|---|---|
LURIA_ROOT | the project root, overriding the search for luria.toml / .git. What lets the test suite run against fixture trees, and what a CI job uses when it checks out a project somewhere other than the working directory. |
LURIA_JOBS | caps the thread pool that renders views, scans files and probes remote URLs. LURIA_JOBS=1 is the serial escape hatch — the one to reach for when a failure needs a deterministic ordering to reproduce. |
Running one module without the package
Every command is a plain function, and the modules stay runnable standalone:
python -m luria.ref_status --all
That is for a project that vendors a single file rather than installing Luria. The CLI is the supported surface; this is the door left unlocked.
What is not configurable
Said plainly, because a reference that only lists dials reads as though everything is one:
- The lint checks themselves. They are functions, not a registry — a
project cannot add a check or switch one off. What it can do is promote
warning classes to failures with
[luria.lint] fail_on, and acknowledge individual findings with the comment directives. A check earns its place by being always wrong and mechanically fixable; anything else is a report. - The frontmatter vocabulary.
status:,title:,tags:,date:,version:,history:are fixed field names. Which statuses count as in force is per-scheme (active), but the field they live in is not. - Renaming a scheme in place. Adding
[luria.schemes.RFC]costs one table; renaming an existing scheme, or moving its documents, is currently a manual pass — there is no migration command. The decision that would give it one is still Proposed.
Two earlier entries in this list are gone because they stopped being true
(ADR-047): the shipped ADR scheme can be removed — declare the schemes
family without it — and a declared scheme’s output is unset by omission,
because a declared family no longer inherits from the defaults. Both are
demonstrated and pinned by examples/ in the Luria repository, which CI
runs.