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>
3.6 KiB
| title | modified | tags | |
|---|---|---|---|
| Wiki-Link Semantics | 2026-06-09 |
|
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).
Two layers: body text and the links table
A wiki-link lives in two places that are kept in sync in one direction:
- 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 untilheph migrate-linksis run. - The
linkstable — a materialized index. Every time a body is written,sync_wiki_linksre-extracts the body's links, resolves each target, and diffs the node'swiki-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.
What heph link add <src> <dst> wiki means
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):
- Node id —
[[01ABC…]]resolves to that node if it exists and is live. - Alias — the
aliasestable. - 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.getrewrites[[NODEID]]→[[NODEID|Current Name]], so a rename is always reflected. - Collapse (write path):
update_noderewrites[[NODEID|text]]→[[NODEID]]whentextstill 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.
How links surface in each client
- heph CLI —
heph show/heph contextprint bodies through the expand projection;heph backlinks(via thelinks.backlinksRPC) 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.