Cross-Reference Resolution — Design (Sub-project 5)
Date: 2026-07-06
Status: Approved for planning
Sub-project: SP5 of the Wiki.js → Astro migration (docs/superpowers/specs/2026-07-06-wikijs-to-astro-migration-program-design.md). SP1–SP4 are live at future.buckyverse.org (reader, library/downloads, dictionary, knowledge).
App: apps/reader/ (Astro 5 + pnpm, deploy via wrangler pages deploy).
1. Goal & success criteria
Wire the four live content pillars together by resolving Synergetics section-number (100.00) references to real passage deep-links wherever they are cited — the keystone of Fuller's cross-reference system — and connect concepts to the book chapters they cite. This is the last feature-parity piece before SP6 cutover.
Scope (locked in brainstorming): §-number resolution + concept↔chapter. Deferred: dictionary-term auto-linking (YAGNI — fuzzy, false-positive-prone). Best-effort only: inter-book prose citations (no new work; existing wikilink/relative-link resolution stands).
Done when:
- A §-number cited in a dictionary card, a knowledge article, or a book chapter renders as a link to the exact Synergetics passage (
/read/synergetics/<chapter>#<num>). - Only real Synergetics section numbers link (numbers absent from the map stay plain text — no false links).
- A concept page's §-citations deep-link into Synergetics; a Synergetics chapter page shows the concepts that reference its sections.
- The build stays fully static; file count essentially unchanged (a handful of files + one JSON).
2. The §-map (source & prebuild artifact)
- Source: the Synergetics EPUB already carries §-number anchors — the source's
\label{sec:100.00…}/\label{ch:100.00…}survive into the tex4ht EPUB asid="100.00"(verified live:/read/synergetics/manuscriptch2/containsid="100.00"). Figure labels are distinct (fig:), so figures are not mistaken for sections. - Prebuild:
apps/reader/scripts/build-section-map.mjsruns the reader's existingparseEpub(fromsrc/lib/epub/parseEpub) onsynergetics.epub, collects every chapter'sanchors, keeps those whoseidmatches the §-number pattern^\d{3,4}\.\d{2}$, and emitssynergetics/section-map.json:Committed artifact (mirrors the SP3 dictionary shards). Re-run when the Synergetics EPUB changes (parallel content track).{ "100.00": { "chapter": "manuscriptch2", "url": "/read/synergetics/manuscriptch2#100.00" }, ... } - The reader book slug for Synergetics is
synergetics(frombooks.config); the chapter route is/read/synergetics/<chapterSlug>with#<anchor>deep links (SP1).
3. The resolver (src/lib/xref/resolveSectionRefs.ts)
Pure, unit-tested. resolveSectionRefs(html, map): string:
- Scans text nodes (via cheerio, skipping existing
<a>/<code>/<pre>to avoid double-linking) for §-number tokens: bare\b\d{3,4}\.\d{2,3}\b(2–3 decimal digits — some sections use 3, e.g.986.086) and theSec.\s*<num>/Sec\s*<num>forms. The exact key format is whatever the EPUB anchoriduses; the map-membership gate makes the token pattern's breadth harmless. - Membership gate: replace a token with
<a class="xref-sec" href="<map[num].url>">…</a>only ifnumis a key in the map. Tokens not in the map (figure numbers, dates, coincidental) are left untouched. This is the correctness guarantee — no speculative links. - Idempotent (running twice does not double-wrap).
4. Wiring into the three loaders
The resolver is applied during each loader's HTML render, after that loader's own link rewriting:
dictionaryLoader(SP3) — apply inrenderCard(cards cite thousands of §-numbers). Rebuild dictionary.knowledgeLoader(SP4) — apply inrenderArticle(afterrewriteLinks). Rebuild knowledge.bookLoader(SP1) — apply to each chapter's sanitized HTML (Synergetics' internal cross-refs + other books citing Synergetics). Rebuild books.
All three read synergetics/section-map.json via a shared xref.config.ts (path + loaded map). Because the map is a committed prebuild artifact, loaders stay independent (no cross-loader ordering).
5. Concept↔chapter
- Concept → chapter (automatic): concepts are knowledge articles; their §-citations become deep links via §4's knowledge wiring. No extra work.
- Chapter → concepts (reverse index): a build step scans the knowledge concepts for cited §-numbers and builds
§ → [{conceptSlug, title}], grouped by Synergetics chapter, emitted alongside the map (or assynergetics/section-concepts.json). The Synergetics chapter route renders a small "Referenced by concepts" list (skipped when empty). This is the only new page surface; everything else is in-place link resolution.
6. Scope
In: §-map prebuild; resolveSectionRefs; wiring into dictionary/knowledge/book loaders; concept→chapter (auto) + chapter→concepts reverse list; rebuild + redeploy the three sections.
Out: dictionary-term auto-linking; inter-book prose-citation parsing; rebuilding the relationship graph (linked in SP4); any change to the §-anchors in the Synergetics source (we consume what exists).
7. Testing
- Unit (
src/lib/xref/*.test.ts):buildSectionMapFromAnchors: given parsed chapters with anchors incl.id="100.00"andid="x11-13000", returns only the §-number entries with correct urls.resolveSectionRefs: links a mapped100.00; links theSec. 100.00form; leaves an unmapped999.99as plain text; does not double-link inside an existing<a>; idempotent.
- Build smoke:
section-map.jsonhas a plausible count (hundreds of §-numbers); a rebuilt dictionary card that cites a real §-number contains anxref-seclink; a concept page contains a §-deep-link; a Synergetics chapter page shows a "Referenced by concepts" entry. - Live: deploy; a known dictionary card → its §-link resolves (200) into
/read/synergetics/<ch>#<num>and the anchor exists on that page.
8. Risks
- §-anchor coverage/format drift — if some sections lack
id="<num>"anchors, those refs won't link (degrade to text, acceptable). Mitigate: report the map size at build; spot-check a range. - Over-matching — the
\d{3,4}\.\d{2}pattern also matches figures/dates; the map-membership gate is the guard (only real section anchors link). Verified by the "unmapped stays text" test. - Re-render cost — applying the resolver adds a cheerio pass to ~21k dictionary cards + 378 articles + book chapters at build; acceptable at build time (measure; the dictionary is the largest).
- Idempotency across loaders — the resolver must skip existing anchors so it composes with each loader's prior link rewriting.
9. Definition of done
§-numbers resolve to Synergetics passage deep-links across dictionary, knowledge, and books; only real section numbers link; concepts deep-link into chapters and chapters list referencing concepts; the build is fully static with an added section-map.json (+ section-concepts.json); the three sections are rebuilt and redeployed to future.buckyverse.org. This closes feature parity for SP6 cutover.