References (doc#id)
The doc#id cross-reference scheme — repo-global unique ids, dangling-ref detection, and how the resolver keeps the model honest.
Blocks become a connected model through references: any block may carry a
top-level id:, and other blocks point at it as doc#id.
# ```userstory
id: US-142
role: shopper
want: pay in one step
soThat: I can complete my purchase quickly
links:
- { ref: orders-api#seq-place-order, label: Request flow }The scheme
- Any block may carry an
id(a human-readable slug). Ids are repo-global unique. - A reference is
doc#id(absolute) or#id(within the current document).docis the slug — the path under the docs root, stripped of.md(e.g.orders-api, orarchitecture/overview). - A bare
#idmeans the current document — always prefer#idwhen referencing inside the same doc, since it survives renames. If you're editingdocs/orders.mdand want to point atid: seq-place-orderin the same file, writeref: "#seq-place-order", NOTref: "orders#seq-place-order". - A reference whose target id does not exist is a dangling reference and
fails validation. Only add a
refto an id you know exists (or are creating in the same change).
What the resolver does
The resolver builds { id → { doc, block } } and { from, to } edges across
all docs and emits diagnostics for duplicate ids and dangling refs,
each with file + line + offending value:
- Duplicate id →
avo checkfails with both file/line locations (E_DUP_ID). - A
refpointing at an id that doesn't exist →avo checkfails with the file, line, and the offending ref string (E_DANGLING_REF).
CI gates on this naturally: avo check exits non-zero on any error. See
Validation for the full diagnostic taxonomy.
Which fields carry references
Two block types carry references today: userstory (links[].ref) and
stories (items[].links[].ref). The extractRefs function in each block's
registry entry is the single place wiring this — extending another block to
carry references is a small addition to its BlockDef (see
Architecture).