hephaestus/docs/explanation/wiki-links.md
Erich Blume 05212133ac
All checks were successful
Build / validate (pull_request) Successful in 5m33s
docs: task-sweep end state — wiki-links explanation + changelog fragments
Docs-first commit for the feature/task-sweep branch, which addresses the
bulk of the outstanding Hephaestus-project tasks in one sweep. The
fragments describe the intended end state; code follows.

Also delivers the "understand heph link wiki semantics" task outright:
the new [[wiki-links]] explanation card documents body-vs-links-table
layering, the manual-wiki-link reconciliation footgun, resolution tiers,
and per-client surfaces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:33:59 -07:00

3.6 KiB

title modified tags
Wiki-Link Semantics 2026-06-09
explanation

Wiki-Link Semantics

How [[wiki-links]] work in the data model, what heph link add … wiki actually does, and how links surface in each client. Companion to the frozen build record in v1-prototype-tech-spec (§5, §6, §8.4).

A wiki-link lives in two places that are kept in sync in one direction:

  1. The body text of a node — the source of truth. At rest a link is stored canonically as [[NODEID]], or [[NODEID|custom text]] when the author gave explicit display text. Legacy [[Name]] links still resolve by title until heph migrate-links is run.
  2. The links table — a materialized index. Every time a body is written, sync_wiki_links re-extracts the body's links, resolves each target, and diffs the node's wiki-type link rows to match: rows for targets no longer in the body are tombstoned, rows for new targets are added. This index is what powers backlinks.

The direction matters: body → links, never links → body.

heph link add … wiki inserts a wiki row directly into the links table without touching the body. Because the body is the source of truth, the next body write on <src> reconciles the index against the body and tombstones the manual row — the link silently disappears.

So: a manual wiki link is at best a temporary annotation on a node whose body never changes, and at worst a footgun. If you want a durable wiki-link, put [[dst]] in the body. The non-wiki link types (parent, blocks, tagged, in-project, canonical-context, log-of) are only managed explicitly and are never reconciled against bodies — those are the correct use of heph link add.

Resolution (who does [[target]] point at?)

Three tiers, first match wins (tech-spec §8.4):

  1. Node id[[01ABC…]] resolves to that node if it exists and is live.
  2. Alias — the aliases table.
  3. Exact title — excluding canonical-context docs, because a task and its context doc share a title; [[Task Title]] must resolve to the task.

Unresolved targets are not an error: the body keeps the text, no link row is created, and a later body write re-syncs once the target exists.

Display projection: expand on read, collapse on write

Bodies at rest store bare ids; humans read names:

  • Expand (read path): node.get rewrites [[NODEID]][[NODEID|Current Name]], so a rename is always reflected.
  • Collapse (write path): update_node rewrites [[NODEID|text]][[NODEID]] when text still equals the target's current title (an auto-label), preserving genuine custom labels. An unchanged read→write round-trips exactly.
  • Export also expands ids to readable labels, so exported markdown is human-legible outside heph.
  • heph CLIheph show / heph context print bodies through the expand projection; heph backlinks (via the links.backlinks RPC) lists incoming links from the materialized index.
  • heph-tui — the preview pane renders the selected task's canonical-context body (expanded labels). Links are display text only today; there is no follow-link navigation in the TUI yet.
  • PWA — mirrors the TUI's read path through the same hephd RPCs, so it sees the same expanded labels; like the TUI, links are not yet tappable navigation.
  • hephaestus.nvim — the richest surface: buffer-backed context docs round-trip through expand/collapse, so [[id|Name]] spans are editable text that collapses back to canonical ids on save.