ADR-022: url-ok acknowledges foreign codes only

Status Active · Filed 2026-08-04 · Influenced by ADR-007 · ADR-016 · Influenced ADR-023

Context

The url-ok directive acknowledges a link whose label is a composed foreign code but whose URL is hand-written rather than constructed. The question this records came up in review: the name reads generic, so why constrain it to the remote namespace? A contributor might want to hand-target a link to one of their own documents — an absolute URL to the file carrying a local DP, say — and it seems harmless to let them make that explicit the same way.

The check behind the directive is a comparison: the written target against what ADR-016’s machinery would construct for the code in the label. Whether the directive can widen turns on whether that comparison means anything outside the remote namespace.

Decision

url-ok names composed foreign codes only, and the check inspects only links whose label is one. Three reasons, in decreasing order of force:

A warning needs a well-defined wrong. A foreign code has exactly one constructed URL — that is the whole design of the remote namespace: one config entry, three resolution rungs, one answer. “The written target differs from the construction” is therefore a meaningful binary. A local code has a family of legitimate targets that depends on where the citing text renders and which aspect is being cited — the source file, the index row, an anchor in the assembled principles document, a book anchor for a journal entry. A check that adjudicated among those would flag correct links routinely, and a warning that is usually noise gets acknowledged by reflex — at which point the acknowledgement stops carrying information, which is the failure mode ADR-007’s report-don’t-fail philosophy exists to avoid.

The risk profiles are opposite. A local link is guarded by existence: its relative target either resolves or visibly breaks, the person who moves a local file can fix every inbound link in the same commit, and the tests sweep them. A remote hand URL fails silently — nothing in this repository breaks when the other project moves a file, renames a heading, or adopts the convention the hand URL was working around. The directive exists precisely to compensate for that invisibility; extending it to links that already fail loudly would spend annotation on a problem the filesystem solves.

The local want has a better answer, and the repo already models it. A project that wants stable absolute citations to its own documents registers itself as a remote — Luria’s own LU prefix exists for exactly this, so the scaffold can cite Luria’s record by URL with the machinery’s guarantees. The citation is then a composed code with a construction and a check behind it; and where the construction genuinely runs out (a document that is a section, not a file), url-ok applies — because the code is now foreign-shaped. The escape hatch composes with the mechanism instead of bypassing it.

The first reason deserves its sharpest form, because the obvious rejoinder is that a hand URL looks like the same anti-pattern in both namespaces — a defect with a mechanical remedy. It is not the same. A local hand URL restates a fact the machinery already owns: the fixer can compute the correct relative target from the repository itself, with zero new information, so the hand version is redundant and the remedy is a rewrite. A foreign hand URL exists precisely because the construction lacks information — which document carries the code, what its anchor is. The human is supplying a fact that has no home in the machinery. Same-looking artifact, opposite information content; an annotation is the honest way to hold a fact that has nowhere better to live, and dishonest where the fact is redundant.

Which points at what url-ok is for, beyond any single citation: each acknowledgement’s reason is a description of a gap in what remote configuration can express, and a recurring shape is the cue to grow the config rather than keep annotating. The worked example is exact: strata-g is a Luria project on a legacy layout, and its principles are a document-rendered scheme — a shape Luria itself has. A remote on current conventions has a constructible target for a DP code (docs/design-principles.md#dp-18; the stable #dp-N anchors exist for exactly this), so the gap is per-scheme remote mappings, not anything essential about foreignness. What survives even that feature is the residue: strata-g’s legacy anchors are heading-derived, no template can produce a slug from a code, and discovery cannot read a private repository. The residue is url-ok’s permanent jurisdiction; everything else it holds is a bridge, kept visible until the config learns the shape.

Alternatives considered

  • Any known code, local included. Requires first defining a canonical target for every local citation, which would adjudicate among legitimate variants and drown the report in false positives. The directive vocabulary maps names to checks; a name whose check is mostly wrong teaches people to suppress.
  • A second directive for local hand URLs. An absolute URL to a file in the same repository freezes a ref, bypasses the relative-link guarantees, and breaks on the next branch rename — it is a defect with a mechanical remedy (a relative link the fixer will happily write), not a deliberate state to acknowledge. An annotation for it would bless an anti-pattern.
  • Grow the remote model now, instead of adding the directive. The right direction and the wrong sequencing. Per-scheme remote mappings would let a Luria-shaped remote’s DP codes construct, and that feature should follow — but it cannot retire the directive, because the residue (legacy heading-derived anchors that need a slug no template can compute, private repositories discovery cannot read) still needs an acknowledged home. Build the escape hatch first, let its accumulated reasons specify the feature.
  • No check at all (the state before this PR). The hand URL is a projection frozen at writing time (DP-3); invisible was how it got written wrong once already — the constructed guess that pointed at a file which never existed is what ADR-016’s “discovery is authoritative” rule answers.

Consequences

  • The directive vocabulary keeps its shape: every name maps to exactly one check, and a directive that stops applying reports itself. url-ok’s scope is the scope of the construction it overrides.
  • A contributor who wants a hand-targeted local link just writes one — the lint has never demanded anything of an existing local link, so there is nothing to acknowledge. If a canonical-target check for local links is ever wanted, that is a new check first; whether it earns an acknowledgement directive can be judged when its false-positive rate is known.
  • Anyone asking “why can’t I url-ok my own DP?” should hear the short version: register yourself as a remote and cite the composed code — you get the check instead of exempting yourself from it.
  • When per-scheme remote mappings land, existing acknowledgements whose gap the config now covers become fixable: delete the hand URL, let the citation construct, and the stale-directive report retires the url-ok — the full loop, closing the way it was designed to.