ADR-016: Remotes are public URLs, Luria is its own worked example, and every scheme versions
Status Active · Filed 2026-08-03 · Supersedes ADR-015 · Influenced ADR-022 · ADR-023 · ADR-024
Context
ADR-015 established that a foreign record gets a prefix and the config turns it into a URL. That part was right and is kept. Two things around it were not.
The local-clone discovery path. To resolve the ancestor project — private,
and with title-slug filenames that predate every convention this package
settled — discovery could read a checkout at path = "../strata-g" and commit
the resulting code→filename map. It worked, and the objection to it is not that
it failed: it is that a resolution that depends on what happens to be on
somebody’s disk is not reproducible. The lockfile it produced could not be
regenerated by anyone without that clone, which makes a committed artifact that
nobody else can rebuild — the same property that makes a hand-maintained
projection untrustworthy (DP-3).
It was also the tail wagging the dog. The ancestor was the pilot; its record is the shape this package was extracted from, not a shape the package should contort to serve. The intended path is to port that record to Luria once this stabilises, at which point it needs no special handling at all.
No worked example inside the package. With the ancestor’s remote removed, the mechanism would have had no user but its own tests — and a feature exercised only by tests is a feature whose integration is unverified.
Decision
Discovery reads public repositories over HTTPS, and nothing else. path is
gone from Remote. A remote Luria cannot read is told so and left on the
code-only filename convention, or given an explicit url template. There is
deliberately no credential path: a discovery that needs a secret is a discovery
CI cannot reproduce, which is the problem the clone option had.
Luria registers itself as remote LU. Not a curiosity — it has a real
consumer. The luria init scaffold’s templates used to carry pasted
https://github.com/dmarx/luria/blob/main/… URLs so a new project could reach
the reasoning behind a convention; they now cite LU-ADR-013 and the machinery
writes the link. A scaffolded project gets followable pointers, and this package
uses its own remote mechanism to give them.
It also means LU needs no lockfile: Luria follows
ADR-013, so the code is the filename and rung 3 is exact. The
dogfood therefore demonstrates the convention paying for itself.
version: is standard frontmatter for every scheme
ADR-012 gave principles a version because they are living
documents. Decisions turn out to want the same field for a narrower reason.
A decision that changes is superseded, never edited — that rule stands. But
a decision whose scope widens without its choice changing is a revision, and
a reader needs to tell that apart from a fresh decision. The version says which
revision of the claim they are reading; history: says what moved.
It renders in the index only when it is not 1. A column of ones teaches nothing, and the field exists precisely so that a document which has been revised says so where it is read.
*.stub files are linted
A stub is the hand-written prose of a generated view. The lint skipped it for
not being markdown, and skipped the rendered page for being generated — so a
bare reference written in a stub was invisible to both checks at once. Found by
writing exactly that: is LU-ADR-001. in the scaffold’s index stub, rendered
unlinked into the index, reported by nothing. link_base already knew where a
stub renders, so this was one glob.
Alternatives considered
- Keep the clone path behind a flag. The reproducibility problem is identical whether the option is default or opt-in, and an escape hatch that produces a committed file others can’t regenerate is worse than none — someone will use it and nobody will know why the lockfile can’t be rebuilt.
- A credentialed fetch (token in the environment) for private remotes.
Solves discovery and moves the problem: now the lockfile is reproducible only
by people holding a secret, and CI needs one to verify a documentation
check. The honest answer for a private remote is a
urltemplate. - Register no remote at all and leave the mechanism to its tests. Cheaper,
and it leaves the integration unverified — the
*.stubhole above was found by wiringLUinto the scaffold, not by any test. - Version only principles, as ADR-012 had it. Defensible: a decision is superseded rather than revised, so the field is usually 1. But a field that exists in one scheme and not another is a special case every reader has to learn, and the render already hides the uninteresting case.
- Rewrite ADR-015 in place. It is hours old, and the temptation was real. The rule says supersede, and the rule is right: a record you can rewrite is a record that can’t be trusted about what you used to think — including about a decision that lasted an afternoon.
Consequences
- The ancestor’s decisions are no longer cited by code anywhere. ADR-009 names the count and says why they are not enumerated, which is more honest than eight numbers nobody can follow.
remotes.lock.jsonis deleted. Nothing in this repo needs one; the file returns the day a remote with slug filenames is registered.luria remotes --checkis now genuinely useful here, becauseLUis public: it verifies the scaffold’s pointers still resolve, which the pasted URLs it replaced never did.- A decision superseded within a day is itself the record working. This is the second Superseded entry in the corpus, and the first where the supersession happened fast enough that the temptation to edit in place was the interesting part.