ADR-005: References in prose are hyperlinks, enforced by lint

Status Active · Filed 2026-08-03 · Influenced ADR-025 · ADR-042 · DP-003 · DP-004 · DP-005

Context

A record whose whole value is being navigable was not navigable. In strata-g, SG-ADR-053 appeared as a link about sixty times and as bare text about thirty more — sometimes in the same paragraph. Following a bare one meant leaving the page, globbing the decisions directory, and coming back.

The drift wasn’t toward “unlinked”. It was toward random, which is worse: a reader can’t learn which references are worth clicking, so they stop clicking.

Decision

Every resolvable reference in prose is a hyperlink, and the lint fails on the ones that aren’t.

One scanner backs both the check and the fixer, so luria lint’s failure message is a command rather than a puzzle, and the two can’t disagree about what counts (DP-4).

A reference only counts in prose. Code fences and spans, HTML comments, bare URLs, both halves of an existing link, defined shortcut references, HTML tags — all masked. Four rules look like implementation detail and are load-bearing:

  1. Raw HTML blocks get <a href>, not […](…). Markdown isn’t parsed inside an HTML block, so a markdown link written there renders as literal brackets.
  2. Backtick pairing is per paragraph. Pairing code spans document-wide lets one unbalanced tick invert which side of every later tick counts as code — observed symptom: a reference linked inside its own code span, while 809 real references in the same file were silently skipped as “code”.
  3. An ambiguous low #N is left alone. Docs number more things than issues — principles, open questions, stories. Above the highest principle number it can only be an issue; at or below, it takes an explicit cue or stays bare. A confidently wrong link costs a reader more than a number they look up.
  4. Fragment links resolve from where the text lands, not where the file sits — otherwise every fragment link breaks at collection time, in a bot commit, long after the contribution that wrote it.

Frontmatter is data and stays plain, with one exception: a summary is prose, and the generator rebases its links per output (ADR-004), so it carries them. That rewrite is verified against the YAML — it must still parse, leave every other key byte-identical, and reduce back to the original when links are stripped — rather than assumed safe.

Alternatives considered

  • Absolute URLs everywhere. Uniform, and immune to the fragment problem. Rejected: it breaks reading the docs offline or in any renderer that isn’t the forge, and most reading — including by agents — happens in an editor.
  • A convention with no lint. Exactly the shape that produced the drift.
  • Autolink at render time. The forge renders these files and we don’t own that pipeline; the source would stay bare.
  • Warn instead of fail. A bare reference is mechanically fixable and always wrong, which is precisely the case where failing is right — contrast ADR-007, where it isn’t.

Consequences

  • Writing see ADR-004 in a new document is a lint failure with a one-command fix.
  • The masking rules need tests more than the checks do: a false positive is a failure a contributor can’t fix by hand, and a false negative is a reference that stays bare forever.
  • The fixer is idempotent, which is what makes it safe to run corpus-wide.