ADR-046: Reference detection is scheme-driven, not three hardcoded patterns
Status Active · Filed 2026-08-11 · Influenced by ADR-006 · ADR-045
Context
ADR-006 decided that a referable document family is a
[luria.schemes.X] entry rather than a hardcoded prefix, and the vocabulary
followed: the acknowledgement verb is inactive-ok, not adr-ok. Rendering
honoured it, luria new honoured it (ADR-036), and the
configuration reference (ADR-044) described it.
The linter did not. doc_refs.find_refs matched exactly three patterns:
for kind, regex in (("dp", DP_RE), ("adr", ADR_RE), ("issue", ISSUE_RE)):ADR_RE is a module constant spelling ADR. DP_RE matches the prose
spelling — design principles #17 — and has no branch for the code spelling
DP-17. resolve matched the same three kinds, with adrs meaning the ADR
scheme’s documents and the fallthrough meaning principles.
Two consequences, both invisible until someone ran a configuration that used them:
- A configured scheme was never checked.
examples/rfcs-and-specsrenders an RFC index and a SPEC document, scaffolds withluria new rfc, and citedSPEC-001from an RFC in plain prose.luria lintpassed. The reference was neither linked nor reported — the state the whole package exists to prevent. DP-6was a documented lie.CLAUDE.mdand the scaffolded template both say to write the bare code and letluria link --fixspell the target, namingDP-6explicitly. A bareDP-6matched nothing, so it was never linked and never flagged: the worst of the three possible behaviours, because silence reads as approval. Applying the fix to this repository linked 38 references that had accumulated unseen.
Neither was found by review. Both were found in the first minute of running a worked example, which is the argument ADR-045 makes.
Decision
find_refs builds its pattern list from current().schemes, tagging each
match with kind="scheme" and the prefix it matched under. Ref gains
prefix; kind collapses to "scheme" | "issue" | "remote".
DP_RE still runs, first and separately, because it matches the prose
spelling that no scheme pattern covers — design principles #17, and the
sibling chain #17 and #18. The code spelling arrives with the schemes,
like every other prefix. Remotes still claim their spans before any local
pattern, so LU-ADR-013 is never read as a local ADR-013
(ADR-016).
resolve dispatches on the scheme’s render mode: an index-rendered scheme
resolves to the document’s own file, a document-rendered one to an anchor in
the assembled page. Discovered anchors win over constructed ones where they
exist, which keeps working the project whose principles are still one
hand-written file with heading-derived anchors (ADR-012).
One guard is load-bearing and was not obvious. A document never links to
itself. For an index-rendered scheme that falls out of target == source,
because the document is the file being scanned. For a document-rendered one
the source is the fragment and the target is the page it assembles into —
different files, so the existing test does not fire. Without an explicit
check, every principle’s own # DP-001: heading became a link into the
assembled document, and check_title then failed on a heading that no longer
matched its frontmatter. That is what the first run did, across every
principle at once.
Alternatives considered
- Add
RFC_RE,SPEC_REbesideADR_RE. The same mistake with more constants, and it cannot work: the prefixes are not known until a project’s config is read. - One
Ref.kindper scheme ("rfc","spec"). Reintroduces exactly what ADR-006 rejected —ADRas a special name in the vocabulary — and everykind ==comparison becomes a lookup against config anyway. - Fix
DP_REonly, since that was the reported symptom. Would have left every other configured scheme unlinted, and the report was a symptom of the general defect, not the defect. - Delete the claim from the guide instead.
docs/adopting.mdsaysRFC-7is a first-class reference thatluria link --fixwrites andluria lintdemands. Making the sentence true by removing it trades a feature the package already promised for an accurate paragraph. - Status quo. The cost is not hypothetical: 38 unlinked references in this repository alone, and any adopter with a second scheme running an unchecked record while the badge said clean.
Consequences
Every configured scheme is now linted, linked and reported on equal terms, and cross-scheme references resolve in both directions — an RFC citing a SPEC gets an anchor into the assembled page, a SPEC citing an RFC gets a file link, each from the base where its text renders.
This repository gained 38 links in one pass. Read a sample rather than trusting it wholesale — that advice is already in the adoption guide, and following it is what caught the self-linking headings before they were committed.
An adopter upgrading will see the same effect at their own scale: references
their record has been carrying unchecked become violations at once. That is
the correct direction — they were always wrong — but it is a one-time
luria link --fix pass that wants reading, not a silent upgrade. New warnings
are also expected where a scheme code resolves to nothing; two illustrative
codes in doc_refs.py’s own prose needed an unresolved-ok directive for
precisely this reason.
ADR_RE stays a module constant. The code-comment scan expects that shape, and
that scan is a different question from prose references; generalizing it is
its own decision, and not one to make while it is not the thing being fixed.