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 enforce retired-citations while legitimately carrying pending-documents for 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_on instead.

Consequences

  • ADR-007 is Superseded by 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.toml documents 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 because fail_on names it — the remedy (acknowledge or fix) is unchanged and stated where it always was.