Found while routing in-body note links through the router. That change fixed the full page reload for wikilinks, which are the only note links Hatchdoor resolves. A note link written the plain-Markdown way is a different problem: it has never worked, and it does not merely reload, it lands nowhere.
What happens
A link like [Beacon Launch](Beacon%20Launch.md) renders as a link, is clickable, and goes to a dead end. The href is left exactly as the author wrote it, so the browser resolves it against the current note's URL.
Measured in Chromium against just dev-start, from a note in 00-inbox linking to 20-projects/Beacon Launch.md:
| written in the note |
href in the page |
where the click lands |
what the reader sees |
[[Beacon Launch]] |
/v/<id>/n/beacon-launch |
the note |
works, router navigation |
[x](Beacon%20Launch.md) |
Beacon%20Launch.md |
/v/<id>/n/Beacon%20Launch.md |
full page reload, then "Note not found: Beacon Launch.md" |
[x](20-projects/Beacon%20Launch.md) |
20-projects/Beacon%20Launch.md |
/v/<id>/n/20-projects/Beacon%20Launch.md |
full page reload, then a blank pane: no route matches, so nothing renders and no error is shown |
[x](../20-projects/Beacon%20Launch.md) |
../20-projects/Beacon%20Launch.md |
/v/<id>/20-projects/Beacon%20Launch.md |
same blank pane |
The blank-pane cases are the worse ones. The sidebar is there, the main pane is empty, and nothing says why.
These links are also invisible to backlinks, the Links panel, the graph and statistics, so a vault using them looks unlinked even where it is densely cross-referenced.
Note that a target with a raw space, [x](Beacon Launch.md), is not a link at all under CommonMark and renders as literal text. Only the percent-encoded form reaches this path, which is the form Obsidian writes.
Cause
Two places understand [[ ]] and nothing else.
extract_wikilink_targets in src/vault/links.rs:82 scans each non-code line for [[ and collects what follows. It feeds outgoing links and backlinks, and through them the Links panel, the graph and the link statistics. A Markdown link contributes nothing to any of them.
rewriteWikilinks in frontend/src/components/note-page/wikilinks.ts:50 matches /(!?)\[\[([^\]\r\n]+)\]\]/g and rewrites a resolved hit into /v/<vault>/n/<slug>. A Markdown target is never sent to resolve-batch and never rewritten.
By the time the href reaches the markdown renderer's a component in frontend/src/components/note-page/renderers.tsx, it is a relative filename. It is not a sentinel, not external, not a PDF and not a note route, so it falls to the plain anchor branch and the browser takes it from there.
Who hits this
Obsidian's Files and links settings carry a Use [[Wikilinks]] toggle. With it switched off, Obsidian writes every new link in the Markdown form with a percent-encoded path. A vault authored that way has no working note links in Hatchdoor at all, and no backlinks either, while looking completely normal in Obsidian.
Suggested direction
The resolver already knows how to answer this. normalize_link_target in src/vault/paths.rs:29 strips a .md suffix and normalises separators, and resolve_target_slug matches a target by path-title or by basename. A Markdown target is exactly the shape those two already accept. The missing step is recognising it as a note reference in the first place.
Two decisions to take before writing anything:
How far the fix goes. Rewriting the href in wikilinks.ts makes the links navigate and is frontend-only. Teaching extract_wikilink_targets about the Markdown form as well makes them count as links, which is more correct but changes backlinks, the graph and the statistics for every existing vault, and needs a decision about whether that shift is wanted.
What an unresolved or non-note target does. A wikilink that resolves to nothing renders as a broken-link span rather than a link. A Markdown target that names no note should presumably do the same, but that means deciding what "names no note" is for a target that could equally be an attachment. Related: the .pdf branch already rewrites a relative PDF target to the asset endpoint, while other attachment extensions, .mp4 among them, fall through to the same bare anchor and the same dead URL. Worth settling both in one pass, since it is one classifier.
Scope
Navigation only: frontend/src/components/note-page/wikilinks.ts, frontend/src/components/note-page/renderers.tsx, and their tests.
Including link records: src/vault/links.rs and its resolver, plus whatever the note-links contract change pulls in. That path needs the interface-change checklist and probably an ADR, since backlinks are a supported contract and existing vaults would see their link counts move.
Priority note
Low, with a caveat. Nothing regressed, this has never worked, and a vault authored with wikilinks throughout never produces the shape. But it is silent: the links look right, the blank-pane variant reports nothing, and someone arriving from an Obsidian vault with wikilinks turned off would find every link in their vault broken with no message explaining it. That makes it worth a decision even if the fix waits.
Found while routing in-body note links through the router. That change fixed the full page reload for wikilinks, which are the only note links Hatchdoor resolves. A note link written the plain-Markdown way is a different problem: it has never worked, and it does not merely reload, it lands nowhere.
What happens
A link like
[Beacon Launch](Beacon%20Launch.md)renders as a link, is clickable, and goes to a dead end. The href is left exactly as the author wrote it, so the browser resolves it against the current note's URL.Measured in Chromium against
just dev-start, from a note in00-inboxlinking to20-projects/Beacon Launch.md:[[Beacon Launch]]/v/<id>/n/beacon-launch[x](Beacon%20Launch.md)Beacon%20Launch.md/v/<id>/n/Beacon%20Launch.md[x](20-projects/Beacon%20Launch.md)20-projects/Beacon%20Launch.md/v/<id>/n/20-projects/Beacon%20Launch.md[x](../20-projects/Beacon%20Launch.md)../20-projects/Beacon%20Launch.md/v/<id>/20-projects/Beacon%20Launch.mdThe blank-pane cases are the worse ones. The sidebar is there, the main pane is empty, and nothing says why.
These links are also invisible to backlinks, the Links panel, the graph and statistics, so a vault using them looks unlinked even where it is densely cross-referenced.
Note that a target with a raw space,
[x](Beacon Launch.md), is not a link at all under CommonMark and renders as literal text. Only the percent-encoded form reaches this path, which is the form Obsidian writes.Cause
Two places understand
[[ ]]and nothing else.extract_wikilink_targetsinsrc/vault/links.rs:82scans each non-code line for[[and collects what follows. It feeds outgoing links and backlinks, and through them the Links panel, the graph and the link statistics. A Markdown link contributes nothing to any of them.rewriteWikilinksinfrontend/src/components/note-page/wikilinks.ts:50matches/(!?)\[\[([^\]\r\n]+)\]\]/gand rewrites a resolved hit into/v/<vault>/n/<slug>. A Markdown target is never sent toresolve-batchand never rewritten.By the time the href reaches the markdown renderer's
acomponent infrontend/src/components/note-page/renderers.tsx, it is a relative filename. It is not a sentinel, not external, not a PDF and not a note route, so it falls to the plain anchor branch and the browser takes it from there.Who hits this
Obsidian's Files and links settings carry a Use [[Wikilinks]] toggle. With it switched off, Obsidian writes every new link in the Markdown form with a percent-encoded path. A vault authored that way has no working note links in Hatchdoor at all, and no backlinks either, while looking completely normal in Obsidian.
Suggested direction
The resolver already knows how to answer this.
normalize_link_targetinsrc/vault/paths.rs:29strips a.mdsuffix and normalises separators, andresolve_target_slugmatches a target by path-title or by basename. A Markdown target is exactly the shape those two already accept. The missing step is recognising it as a note reference in the first place.Two decisions to take before writing anything:
How far the fix goes. Rewriting the href in
wikilinks.tsmakes the links navigate and is frontend-only. Teachingextract_wikilink_targetsabout the Markdown form as well makes them count as links, which is more correct but changes backlinks, the graph and the statistics for every existing vault, and needs a decision about whether that shift is wanted.What an unresolved or non-note target does. A wikilink that resolves to nothing renders as a
broken-linkspan rather than a link. A Markdown target that names no note should presumably do the same, but that means deciding what "names no note" is for a target that could equally be an attachment. Related: the.pdfbranch already rewrites a relative PDF target to the asset endpoint, while other attachment extensions,.mp4among them, fall through to the same bare anchor and the same dead URL. Worth settling both in one pass, since it is one classifier.Scope
Navigation only:
frontend/src/components/note-page/wikilinks.ts,frontend/src/components/note-page/renderers.tsx, and their tests.Including link records:
src/vault/links.rsand its resolver, plus whatever the note-links contract change pulls in. That path needs the interface-change checklist and probably an ADR, since backlinks are a supported contract and existing vaults would see their link counts move.Priority note
Low, with a caveat. Nothing regressed, this has never worked, and a vault authored with wikilinks throughout never produces the shape. But it is silent: the links look right, the blank-pane variant reports nothing, and someone arriving from an Obsidian vault with wikilinks turned off would find every link in their vault broken with no message explaining it. That makes it worth a decision even if the fix waits.