ADR-035: Status enforcement is a dial — reported by default, failable by configuration
Status Active · Filed 2026-08-07 · Issue #40 · Supersedes ADR-007 · Influenced DP-010 · DP-012
Context
ADR-007 established that document status is reported, never
enforced — citing a Rejected decision is often exactly right, and a guard
that is wrong most of the time gets suppressed. That reasoning is sound and
survives here as the default posture.
But its “never able to fail a build” was a constitution where a default would do. Review on the ADR-030 work named the cost (#40): the record’s own ladder walks norms from prose to convention to mechanism to guarantee (DP-5), and a decision that forbids the last rung for status findings is a soft gate against downstream projects that want exactly that promotion — a team that considers an unacknowledged citation of a retired decision a defect should be able to make their build say so.
Decision
Warnings stay the default. Nothing changes for a project that configures nothing; every ADR-007 argument for warn-first is inherited as the argument for warn-by-default.
[luria.lint] fail_on promotes named classes to failures:
[luria.lint]
fail_on = ["retired-citations", "pending-documents"]The vocabulary is the warning classes themselves: retired-citations,
unresolved-codes, hand-written-urls, stale-directives,
pending-documents, unlinted-files. A promoted class’s rows are appended
to the lint’s violations — same detail lines, different consequence — and
both paths read one computation, so they cannot disagree about what a class
contains.
The accounting is untouched. Only unacknowledged rows ever reach a
class, so inactive-ok:, unresolved-ok: and url-ok: keep working under
enforcement — they become the mechanism for stating a deliberate exception
to a rule that now has teeth, which is what an acknowledgement was always
for. The dial changes the consequence, not the bookkeeping.
A wrong notch is an error. fail_on = ["retired-refs"] (no such class)
is itself a lint failure naming the known vocabulary — a dial set to a notch
that doesn’t exist must not silently enforce nothing (DP-1).
Alternatives considered
- Keep “never” (status quo). The superseded position. Its honest core — these findings are usually legitimate — is preserved as the default; what it wrongly encoded was that no project may ever decide otherwise.
- Per-invocation flags (
luria lint --strict-pending). Policy would live in whoever typed the command: CI and a laptop enforcing different rules is the drift this record exists to prevent. Policy belongs in the committed config, versioned with the record it governs. - One boolean
strict = true. The classes fail for different reasons on different projects — a docs-heavy repo may enforceretired-citationswhile legitimately carryingpending-documentsfor months. All-or-nothing recreates ADR-007’s original objection at the other pole. - A ratchet on a checked-in baseline. ADR-007 rejected it for the DP-2
shared-file lock, and that still holds; a project that wants a hard floor
can now set the floor to zero with
fail_oninstead.
Consequences
- ADR-007 is
Supersededby this decision, body intact. Its reasoning is quoted here because it is the reasoning for the default. - This repository’s own config sets no
fail_on— the dogfood posture stays warn-only, deliberately, so the default path is the exercised one. - The scaffolded
luria.tomldocuments the knob in a comment, so an adopter learns the dial exists at the moment they are looking at lint policy. luria lint’s output for a promoted class is its warning text plus the same detail rows, marked as failing becausefail_onnames it — the remedy (acknowledge or fix) is unchanged and stated where it always was.