Context
A scheme rendered as one assembled document gives each of its sources two
addresses: the source file, and an anchor in the page it assembles into. Only
the anchor was reachable — doc_refs resolved every citation of such a code to
output#anchor — and only one of the two was a page, because publishable()
excluded any source whose links are spelled for somewhere else.
That anchor was unreachable where this record is published. Measured on a real v4.5.2 build of it: 330 links across 81 pages, none of which resolved.
The cause stated here in v1 was wrong, and the correction is worth reading
before the rest of this decision (ADR-099). v1 said Quartz “drops the
element and slugifies each heading’s own text instead”. It does not: the <a>
is in the published HTML and always was. What the generator emitted was
<a name="dp-3"></a>, and a name is reachable only on a real navigation —
Quartz is a single-page app whose router scrolls with
document.getElementById, which finds an id and nothing else. One attribute.
The generator emits id now.
What stands, and is why this decision is still the right one: the links worked
in the repository and failed on the site, which is the worst place for a
difference to live. Every check this project runs reads markdown, and
docs/design-principles.md resolves perfectly as a path — so the defect was
invisible on every surface a contributor looks at, which is an argument about
where a citation should point regardless of why the anchor failed.
Decision
Two halves, and neither works without the other.
A document-rendered scheme’s sources are published as pages. publishable()
excluded them by a derived rule — link_base(path) != path.parent asks “are
this file’s links spelled for somewhere else?” — and a design principle answered
yes for the same reason a changelog fragment does. It is nothing like one:
numbered, titled, statused, versioned, cited by code, carrying typed edges.
Every property a decision has except an address. So the rule narrows to what it
always meant — a fragment is not published; a document that happens to
render as a section of one now is. The assembled view is still published; both,
like a decision and its index.
Their links are re-spelled on the way out (_rebase), not in the repository. A
principle’s source writes ../record/decisions.d/ADR-006.md, correct from
docs/ and wrong from record/principles.d/; staging re-points each relative
target as it writes the page. Keeping the source as it is matters — the fixer
and the lint both expect that spelling, and luria lint would fail the moment
it stopped.
[luria.schemes.X] cite chooses where a citation points. "page" for the
document’s own file, "view" for output#anchor. Unset resolves to what the
scheme already does — "view" for a document scheme, "page" for an index
scheme, which has no second address — so the key is inert until a project sets
it. This record sets "page".
Two refusals, because they are different mistakes. An unknown word is a typo. An
explicit cite = "view" on an index scheme is a request that cannot be honoured
— there is no assembled document to anchor into — and resolving to the page
anyway would answer a question the project did not ask.
luria repair moves the links a record already wrote. Flipping the key
governs every citation written from then on and nothing already on disk, because
those are plain markdown links and the linkifier spells bare references. It
rewrote 98 files here. retarget_view_citations is deliberately narrow: the
link TEXT is the author’s sentence rather than a field; a link with no fragment
means the whole assembled document and is left alone; and an anchor naming no
document is left alone, because rewriting it would swap a dead fragment for a
dead FILE, which is worse and hides it from the lint.
Alternatives considered
-
Ship the key without publishing the sources. This was the first shape of this change, and it is wrong in a way that only staging can see. A page target points at
record/principles.d/DP-003.md; if that file is not published, the site does the only thing it can and sends the reader to the repository. Measured:cite = "page"with the oldpublishable()took links redirected to source from 10 to 195. That is a downgrade from the bug — a dead anchor at least lands you on the principles document with the content on it, at the wrong scroll position. Hence both halves, in one change. -
Emit anchors that survive the publisher. Keeps every existing link working and needs no rewrite. Rejected on a false premise, and it was the right answer (ADR-099): nothing was dropping the anchors, so there was no spelling to re-find per publisher.
idis the spelling, every renderer has agreed on it since HTML 4, and the change is one attribute in two emitters. Taking it would not have made this decision unnecessary — a citation landing on the whole principles document at the right scroll position is still worse than landing on the principle — but it would have been done first and cheaply, andcitewould have been a preference rather than a workaround. The original reasoning follows as written: rejected on where the fix would have to live — the anchors are already correct as markdown, and what drops them is a generator’s HTML handling — so the repair means finding a spelling Quartz keeps today, and re-finding it for the next publisher and the next version, in a place where the failure is invisible from the source. A page is a page in every renderer. -
Default
citeto"page"for everyone. Now defensible, since the sources are pages for every project and not just this one. Still declined: it would rewrite an adopter’s citations on their nextluria repairwithout their asking. The key exists to make that a choice, so making it a silent upgrade would be odd. -
Retarget as a one-shot migration rather than part of
repair. Rejected: the rewrite is derivable from the config and the sources, which is the linerepairalready draws — it writes what a generator can decide and leaves what needs judgement to the lint. A one-shot also strands every project that flips the key later.
Consequences
Verified on real Quartz v4.5.2 builds of this record, before and after: 330 broken anchor links across 81 pages → 0, and links to a principle’s page from 85 → 404, all of which resolve. 281 → 307 staged pages. Links redirected to the repository stay flat (10 → 12), which is the check that the new pages absorb the new targets rather than leaking them off-site.
A code naming no document now resolves to nothing where this record cites pages. The anchor was constructed from the number, so a citation of any number produced a link whether or not the document existed; a page target cannot be constructed, so the lint gets to report it.
Two tests changed their claim rather than their threshold.
test_publishable_is_exactly_the_files_whose_links_resolve_in_place asserted
link_base(path) == path.parent over every published file, which was the same
claim as the rule while no exception existed; it now exempts a document source
and says why. And the nested-record test proved its point by the child’s source
being absent, which this legitimately makes present — it now proves it by the
presence of the code alias, since only a config that knows the child’s
scheme can give one.