Docs Decisions
0006 · Inline markdown links with relative .md hrefs
Status: Accepted · 2026-09-22
Context
Documents reference each other, and those references form the graph that drives share suggestions and backlinks. The href format determines whether an export is useful, what a rename costs, and whether a person can write a link without looking something up.
Decision
Ordinary inline markdown, with relative or root-relative .md paths:
See the [onboarding guide](../onboarding.md).
The [deploy runbook](/ops/deploy.md) covers rollback.
Edges are stored with both the href as written and the resolved target_id
(linking.md). Renames never break the graph; only the written text goes stale, and
staleness is surfaced as a decision rather than repaired automatically.
Consequences
- An export is a working file tree. It opens in Obsidian, browses on GitHub, renders anywhere, with every link resolving. This is what makes "easily exportable" mean something.
- Pasting in content from an existing docs repository keeps its links working.
- Links are extracted from the goldmark AST, not a regex — a regex over prose cannot distinguish a link in a paragraph from one inside a fenced code block.
- A rename leaves stale text in the bodies that point at it. Deliberate: auto-rewriting would manufacture a revision on every linking document, attributed to whoever did the rename.
- A link to a document that does not exist yet is
pendingand resolves itself when one appears.
Alternatives rejected
[[wikilinks]]. Familiar, terse, and unambiguous about intent. Rejected because they render as
literal text everywhere outside a wiki-aware editor — an export full of [[foo]] is not a working
document tree.
codna://doc/<uuid> URIs. Never stale, nothing to reconcile. Rejected because raw markdown
becomes unreadable, exports do not work in any external viewer, and writing a link by hand requires
looking up a UUID first.
App-absolute paths (/wiki/engineering/onboarding). Reads well and works as a real URL in the
UI. Rejected because an export is then not self-contained — every link points at a running instance,
which is exactly what export exists to survive.