Chapter 8: Knowledge Graphs and the ADR Lifecycle

Engineering documents do not exist in isolation: an Architecture Decision Record (ADR-0009) supersedes an earlier design (ADR-0001), is amended by a follow-up (ADR-0014), is backed by a benchmark synthesis note, is verified by a code review, and is tracked by an active .todo-list/ task.

AutoDoc (core/index/graph.go, powered by golib/graph) extracts every inline link, resource embed, and typed YAML frontmatter relation into a unified, incrementally maintained knowledge graph inside SQLite.

flowchart TD
    A01["ADR-0001: Legacy Store (status: superseded)"] -->|"superseded_by"| A09["ADR-0009: SQLite WAL Store (status: accepted)"]
    A09 -->|"supersedes"| A01
    A14["ADR-0014: Todo Journal (status: accepted)"] -->|"amends"| A09
    SYN["Synthesis: Benchmark WAL vs Legacy (kind: benchmark)"] -->|"sources"| A01
    SYN -->|"sources"| A09
    REV["Review: Lector Round 1 (verdict: approved)"] -->|"adr"| A09
    REV -->|"wikilink [[benchmark-wal]]"| SYN
    TASK["Todo Task: 2026-10-11-ship-todos"] -->|"adr / review"| REV

1. Unified Link Extraction: Wikilinks, Markdown Links, and Typed Frontmatter

Whenever a file is indexed or modified, AutoDoc extracts three families of directed graph edges (LinkRow in core/index):

1. Obsidian-Style [[wikilinks]] (Kind: "wikilink")

AutoDoc supports the full Obsidian wikilink grammar inside Markdown prose:

2. Standard Markdown Links and Resource Embeds (Kind: "markdown" and "embed")

3. Typed YAML Frontmatter Relations

AutoDoc promotes structured relationship fields in YAML frontmatter into first-class typed graph edges whose Kind equals the frontmatter field name:

Example: Frontmatter Lineage Across Three Linked Documents

# adrs/0009-sqlite-wal.md
---
type: adr
number: "0009"
status: accepted
created: 2026-08-14
tags: [sqlite, storage, wal]
supersedes: [0001-legacy-store]
abstract: "Adopt a single SQLite WAL file per daemon with snapshot readers and a single writer."
---
# adrs/0014-todo-journal.md
---
type: adr
number: "0014"
status: accepted
created: 2026-10-02
tags: [sqlite, todos]
amends: [0009-sqlite-wal]
sources: [synthesis/benchmark-wal.md]
abstract: "Extend the SQLite store with a journaled .todo-list change log and consumer cursors."
---
KindSource_LocationSyntax_ExampleForward_LabelInverse_LabelSearch_Ranking_Effect
wikilinkMarkdown Body[[0203-storage#4-lease|Storage Lease]]LinksBacklinksInbound backlinks apply +0.1 linkBoost authority weight after RRF fusion
markdownMarkdown Body[Storage Lease](../adrs/0203-storage.md#lease)LinksBacklinksInbound backlinks apply +0.1 linkBoost authority weight after RRF fusion
embedMarkdown Body![Architecture](0203-storage/flow.mmd) or ![[./flow.mmd]]EmbedsBacklinksTies companion diagrams and CSV tables to parent document backlinks
supersedesYAML Frontmattersupersedes: [0001-legacy-store]SupersedesSuperseded byMarks target ADR as superseded; demotes target below this document when demote_superseded: true
superseded_byYAML Frontmattersuperseded_by: [0009-sqlite-wal]Superseded bySupersedesDemotes this document's hits below successor 0009-sqlite-wal and sets Hit.SupersededBy
amendsYAML Frontmatteramends: [0009-sqlite-wal]AmendsAmended byLinks incremental ADR amendments to their base decision in Relations drawer
sourcesYAML Frontmattersources: [adrs/0009-sqlite-wal.md]SourcesCited byConnects synthesis and benchmark records to primary evidence documents
relatedYAML Frontmatterrelated: [conventions/code-review.md]RelatedRelatedSymmetric cross-reference edge surfaced in Relations panel (SPC l)
adrYAML Frontmatteradr: 0009-sqlite-wal (or list in todo task)ADRReferenced byBinds code reviews, rationale docs, and .todo-list tasks to governing ADRs
reviewYAML Frontmatterreview: [$KB_ROOT/reviews/lector/pr249-r1.md]ReviewReviewed inBinds .todo-list tasks and PR records to durable review verdicts

2. Deterministic Workspace Link Resolution Rules

When resolving a link (graph.resolve), AutoDoc applies Obsidian-compatible deterministic precedence rules across the workspace:

  1. Exact Workspace-Relative Path: If the target matches a workspace path exactly (adrs/0203-storage.md), it resolves immediately.
  2. Filename Stem, Path Suffix, or Frontmatter aliases: Otherwise, AutoDoc looks up documents whose filename stem (0203-storage), trailing path suffix (adrs/0203-storage), or YAML aliases: entry matches the target.
  3. Nearest-Root Tiebreaker: If multiple files share a stem and one sits closer to the workspace root (fewer / path segments), the shallowest path wins.
  4. Ambiguity Preservation (Reason: "ambiguous"): If two files at the same directory depth match the link, AutoDoc refuses to guess—it marks Resolved: false with Reason: "ambiguous" so graph.unresolved can surface the collision.
  5. Automatic Reactive Re-Resolution: Whenever a new file is created, renamed, deleted, or updates its aliases:, AutoDoc automatically re-evaluates any previously unresolved or ambiguous links that could match that target.
  6. External Todo Symbolic References (Link.External): When a .todo-list/ task references a file outside the current workspace via $KB_ROOT/... or $PROJECT_ROOT/..., AutoDoc resolves the variable (workspace.vars) and populates Link.External with the absolute target path (core/index/graph.go:27-29), never falsely reporting it as a broken workspace link.
Knowledge Graph Link Connectivity Distribution (2,000-Document Engineering KB)0100200300400500600700800900Document Count0 (Orphan)1-2 Links3-5 Links6-10 Links11+ Hub DocsTotal Resolved Graph Degree (Inbound + Outbound Edges)Connectivity Tier0 (Orphan)1-2 Links11+ Hub Docs3-5 Links6-10 Links

3. Navigating the Graph in the TUI, GUI, and Neovim (SPC l)

The Explorer and Its Links Tab (SPC e, SPC l)

The explorer is one panel of two tabs. Explorer (SPC e): every workspace's folders and files, as a tree. Links (SPC l, Relations › Links): what this file is connected to, by kind and direction, then the relations that name nothing and the supersession loops; d there adds the files two links away, under the one that reaches them. Unlike a flat backlinks list, the Links tab groups connections by semantic role and direction:

In Neovim, :AutodocRelations / <leader>ml lists the current file's relations in a picker, a kind each.

It opens over the page, which does not move, from the side the editor's preferences name. SPC e and SPC l open it on their tab, or go to it from the other, and on their own tab close it, as Escape does; Ctrl+PageUp and Ctrl+PageDown change tabs. At the strip's right end, _ hides it to the dock, whose icon shows it again as it was, + maximizes it (again to restore), and x closes it. Enter opens a row, closing the panel from the Explorer and hiding it from the Links. The links' own panel was folded in: where the explorer keeps no place of its own, it opens where the links were left.

On the editor page itself, SPC j goes to the link under the cursor (the document it names, else its web address or file); SPC b b goes back to the previous document, where you left it (Ctrl+O steps back one jump at a time, within a document too); the page's right-click menu has both. In Neovim, :AutodocBack / <leader>mo goes back.

The GUI's Relation Views: GlobeMap and Mindmap

The GUI's relation views (Relations ›, --gui only). GlobeMap: every document of the workspace and the links among them, placed by force, each document's dot coloured by its group (groups are seeded from the schema's types and the folders; l shows their legend) and a superseded one ringed; drag pans, the wheel and + - zoom, 0 fits it again, f cycles every link, body links and relations, and a click opens the document. Relations › GlobeMap settings… and Mindmap settings… choose how each draws. Mindmap: the page's document and those one link from it (d shows two), as a mind map shaped by type; a click opens that document and the map follows it, the keyboard staying on the map. A terminal has the links alone.


4. Querying Links, Backlinks, and Neighborhoods via RPC

AI agents and scripts can traverse the knowledge graph without parsing Markdown files by calling the graph.* RPC domain (core/index/graph.go):

# 1. List all outbound links from a document (optionally filtered by edge kinds)
autodoc --call graph.links '[
  "kb",
  "adrs/0009-sqlite-wal.md",
  {"kinds": ["supersedes", "amends", "wikilink"]}
]'

# 2. List all inbound backlinks pointing to a document
autodoc --call graph.backlinks '[
  "kb",
  "adrs/0009-sqlite-wal.md"
]'

# 3. Extract the 2-hop subgraph (nodes + directed edges) around a document
#    (graph.neighborhood / graph.neighbors & graph.path traversal)
autodoc --call graph.neighborhood '[
  "kb",
  "adrs/0009-sqlite-wal.md",
  2,
  {"kinds": ["supersedes", "superseded_by", "amends", "sources", "adr"]}
]'

# 4. Test how a raw link string resolves from a given source file (saved or unsaved)
autodoc --call graph.resolve '[
  "kb",
  "synthesis/wal-benchmarks.md",
  "[[0009-sqlite-wal#concurrency]]"
]'

Example graph.neighborhood output:

{
  "nodes": [
    "adrs/0001-legacy-store.md",
    "adrs/0009-sqlite-wal.md",
    "adrs/0014-todo-journal.md",
    "synthesis/benchmark-wal.md"
  ],
  "edges": [
    {"src": "adrs/0001-legacy-store.md", "dst": "adrs/0009-sqlite-wal.md", "kind": "superseded_by"},
    {"src": "adrs/0009-sqlite-wal.md", "dst": "adrs/0001-legacy-store.md", "kind": "supersedes"},
    {"src": "adrs/0014-todo-journal.md", "dst": "adrs/0009-sqlite-wal.md", "kind": "amends"},
    {"src": "synthesis/benchmark-wal.md", "dst": "adrs/0009-sqlite-wal.md", "kind": "sources"}
  ]
}

When kinds is passed in options, core/index/graph.go:50-61 pushes the IN (...) predicate directly into the SQLite query before constructing the golib/graph walk—ensuring a 2-hop neighborhood filtered to ["supersedes", "amends"] never traverses an unrelated prose wikilink on the first hop.


5. Superseded ADR Demotion (demote_superseded) and Graph Hygiene (graph.unresolved)

Automatic Demotion of Superseded Decisions in Search

In long-lived engineering organizations, retired ADRs remain in the repository for historical context (status: superseded) and often contain the exact keywords a developer searches for. If a search returns ADR-0001 above ADR-0009, an agent or engineer may accidentally implement the deprecated design.

AutoDoc prevents this with supersession-aware ranking (demote_superseded: true by default on every workspace, overridable per query via retrieval.demote_superseded):

  1. Bidirectional Edge Inference: Whether ADR-0001 declares superseded_by: [0009-sqlite-wal] or ADR-0009 declares supersedes: [0001-legacy-store], AutoDoc resolves the transitive successor along the supersession chain.
  2. Relative Ordering Guarantee: Whenever hits from a superseded document and its active successor both appear in search.query, AutoDoc moves the superseded document's hits below the successor's hits and sets "superseded_by": "adrs/0009-sqlite-wal.md" on each demoted hit.
  3. Cycle Safety: If two documents accidentally declare a circular supersession loop (A supersedes B and B supersedes A), AutoDoc detects the cycle, surfaces it under Supersession loops in the explorer's Links tab (SPC l), and leaves their search order untouched.

Auditing Broken and Ambiguous Links (graph.unresolved)

To keep a knowledge base free of dangling references after file renames or reorganizations, run graph.unresolved:

# Audit all broken or ambiguous links across the workspace
autodoc --call graph.unresolved '["kb"]'

# Audit only broken frontmatter relation links (supersedes, sources, adr)
autodoc --call graph.unresolved '[
  "kb",
  {"kinds": ["supersedes", "superseded_by", "amends", "sources", "adr"]}
]'

Each entry reports {src, raw, reason} where reason is either "missing" (no indexed file matches the target) or "ambiguous" (multiple files at equal depth match the short name, requiring a folder prefix to disambiguate).


← Previous: Chapter 07: Frontmatter Schemas & Validation | Index | Next: Chapter 09: Code Symbol Indexing →