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 old publishable() 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. id is 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, and cite would 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 cite to "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 next luria repair without 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 line repair already 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.