zetl build renders multi-word inline/predicate wikilinks as dead links (raw-title href) despite resolving in graph & check #66

Closed
opened 2026-06-30 07:33:47 +00:00 by anuna-02 · 1 comment
anuna-02 commented 2026-06-30 07:33:47 +00:00 (Migrated from codeberg.org)

Summary

zetl build renders multi-word inline/predicate wikilinks as dead links, emitting an href built from the raw page title (e.g. Gender-Based%20Violence/index.html) instead of the resolved slug path (concepts/gender-based-violence/index.html). The links carry class="link-error wikilink wikilink-dead". This happens even though:

  • the target pages exist,
  • zetl check reports 0 dead links,
  • zetl links / zetl backlinks resolve the same links correctly,
  • the nav / Map-of-Content sidebar (generated from the graph) links to the correct slugs on the same page.

So the graph resolver and the inline HTML renderer disagree. On a deployed static site every natural-language wikilink in prose and every predicate::[[Title Case Link]] is a broken link.

Affects zetl 0.9.0 (HEAD a0fab3b).

Root cause

src/web/markdown.rs, replace_wikilinks_in_segment:

// Look up slug by case-insensitive page name match
let slug = slug_map
    .iter()
    .find(|(k, _)| k.eq_ignore_ascii_case(page))   // `page` = raw target, e.g. "Gender-Based Violence"
    .map(|(_, v)| v.as_str());

slug_map is keyed by page_name (the kebab slug, e.g. gender-based-violence; see build_slug_map in src/web/mod.rs:290). The lookup compares the raw, un-slugified wikilink target against those keys with only eq_ignore_ascii_case — it never converts spaces to hyphens (or otherwise slugifies). So:

"gender-based-violence".eq_ignore_ascii_case("Gender-Based Violence")  // false: '-' vs ' '

The match fails, control falls to the dead-link branch (src/web/markdown.rs:581, also 795/814), and the href is built from urlencoding(page) — the raw title — giving Gender-Based%20Violence/index.html.

Single-token / already-kebab links ([[concept-map]]) match and resolve fine, which is why the bug only shows on multi-word titles.

The graph resolver / zetl check path slugifies the target before resolving, which is why it reports these same links as live. The inline renderer needs the same normalization.

Reproduction

Vault with concepts/gender-based-violence.md and a page that links to it by natural title:

A study of [[Reporting Apps]] and [[Gender-Based Violence]].

- addresses::[[Gender-Based Violence]]
zetl check          # summary: dead_links: 0
zetl links <page>   # target: "gender-based-violence"  (resolved)
zetl build --out-dir dist
grep -o 'href="[^"]*Gender-Based[^"]*"' dist/<page>/index.html
# => href="../../Gender-Based%20Violence/index.html"   (raw title, broken)
#    class="link-error wikilink wikilink-dead"

Observed in the anti-violence-tech vault: e.g. dist/research/datafication-of-metoo/index.html emits
href="../../Apps%20Against%20Abuse%20Challenge/index.html",
href="../../Gender-Based%20Violence/index.html", etc., while the nav on the same page correctly links
../../concepts/gender-based-violence/index.html.

Expected

Inline and predicate wikilinks should resolve to the same slug paths the graph resolver / nav use:
[[Gender-Based Violence]]…/concepts/gender-based-violence/index.html with class="link link-primary wikilink".

Suggested fix

Normalize page with the same slugification the graph/scanner uses before looking it up in slug_map
(rather than eq_ignore_ascii_case on the raw target) in replace_wikilinks_in_segment
(src/web/markdown.rs). Add a regression test alongside the existing wikilink_* tests for a
multi-word, space-containing target.

Impact

Every static build (zetl build) of a vault that uses natural-language multi-word page titles in inline
prose links or predicate::[[...]] links produces broken navigation — the core wikilink feature is broken
for deployed sites, while zetl check gives a false all-clear.

## Summary `zetl build` renders **multi-word inline/predicate wikilinks as dead links**, emitting an `href` built from the raw page title (e.g. `Gender-Based%20Violence/index.html`) instead of the resolved slug path (`concepts/gender-based-violence/index.html`). The links carry `class="link-error wikilink wikilink-dead"`. This happens even though: - the target pages exist, - `zetl check` reports **0 dead links**, - `zetl links` / `zetl backlinks` resolve the same links correctly, - the nav / Map-of-Content sidebar (generated from the graph) links to the **correct** slugs on the same page. So the graph resolver and the inline HTML renderer disagree. On a deployed static site every natural-language wikilink in prose and every `predicate::[[Title Case Link]]` is a broken link. Affects **zetl 0.9.0** (HEAD `a0fab3b`). ## Root cause `src/web/markdown.rs`, `replace_wikilinks_in_segment`: ```rust // Look up slug by case-insensitive page name match let slug = slug_map .iter() .find(|(k, _)| k.eq_ignore_ascii_case(page)) // `page` = raw target, e.g. "Gender-Based Violence" .map(|(_, v)| v.as_str()); ``` `slug_map` is keyed by `page_name` (the kebab slug, e.g. `gender-based-violence`; see `build_slug_map` in `src/web/mod.rs:290`). The lookup compares the **raw, un-slugified** wikilink target against those keys with only `eq_ignore_ascii_case` — it never converts spaces to hyphens (or otherwise slugifies). So: ``` "gender-based-violence".eq_ignore_ascii_case("Gender-Based Violence") // false: '-' vs ' ' ``` The match fails, control falls to the dead-link branch (`src/web/markdown.rs:581`, also 795/814), and the href is built from `urlencoding(page)` — the raw title — giving `Gender-Based%20Violence/index.html`. Single-token / already-kebab links (`[[concept-map]]`) match and resolve fine, which is why the bug only shows on multi-word titles. The graph resolver / `zetl check` path slugifies the target before resolving, which is why it reports these same links as live. The inline renderer needs the same normalization. ## Reproduction Vault with `concepts/gender-based-violence.md` and a page that links to it by natural title: ```markdown A study of [[Reporting Apps]] and [[Gender-Based Violence]]. - addresses::[[Gender-Based Violence]] ``` ```sh zetl check # summary: dead_links: 0 zetl links <page> # target: "gender-based-violence" (resolved) zetl build --out-dir dist grep -o 'href="[^"]*Gender-Based[^"]*"' dist/<page>/index.html # => href="../../Gender-Based%20Violence/index.html" (raw title, broken) # class="link-error wikilink wikilink-dead" ``` Observed in the `anti-violence-tech` vault: e.g. `dist/research/datafication-of-metoo/index.html` emits `href="../../Apps%20Against%20Abuse%20Challenge/index.html"`, `href="../../Gender-Based%20Violence/index.html"`, etc., while the nav on the same page correctly links `../../concepts/gender-based-violence/index.html`. ## Expected Inline and predicate wikilinks should resolve to the same slug paths the graph resolver / nav use: `[[Gender-Based Violence]]` → `…/concepts/gender-based-violence/index.html` with `class="link link-primary wikilink"`. ## Suggested fix Normalize `page` with the same slugification the graph/scanner uses before looking it up in `slug_map` (rather than `eq_ignore_ascii_case` on the raw target) in `replace_wikilinks_in_segment` (`src/web/markdown.rs`). Add a regression test alongside the existing `wikilink_*` tests for a multi-word, space-containing target. ## Impact Every static build (`zetl build`) of a vault that uses natural-language multi-word page titles in inline prose links or `predicate::[[...]]` links produces broken navigation — the core wikilink feature is broken for deployed sites, while `zetl check` gives a false all-clear.
anuna-02 commented 2026-06-30 11:49:17 +00:00 (Migrated from codeberg.org)

Fixed in zetl 0.9.1 (verified earlier); confirmed in 0.9.2 build/deploy. Multi-word inline/predicate wikilinks now resolve to slugs.

Fixed in zetl 0.9.1 (verified earlier); confirmed in 0.9.2 build/deploy. Multi-word inline/predicate wikilinks now resolve to slugs.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
anuna-research/zetl#66
No description provided.