Processing Pipeline
This page explains how the Antora Tracer extension processes content during a build. It covers the internal pass structure, the graph lifecycle, and how components coordinate.
Build integration
The extension hooks into Antora’s event pipeline through two events:
-
contentClassified— fires once after content classification, before document conversion. The extension parses items, expands macros, substitutes links, and registers generated matrices and the per-version graph snapshot in the content catalog. -
sitePublished— fires once after all pages are published. The extension writes the standalone traceability output directory (matrices, coverage, index).
The same RequirementsTraceabilityExtension class works standalone (via CLI) or within Antora — the event handlers are optional.
Pass structure within contentClassified
The extension runs four passes within the contentClassified event.
The order matters — each pass depends on the result of earlier ones.
Pass 1: Parse and populate the graph
DocumentParser scans every page and partial for [item] blocks and inline relationship macros.
Items and relationships are registered in the TraceabilityGraph.
Both pages and partials are processed here.
The graph must be complete before any subsequent pass can resolve cross-file references. This is why Pass 1 processes all files before moving on.
Pass 2: Expand relation macros (pages and partials)
tracer:outgoing[], tracer:incoming[], and tracer:links[] placeholders are replaced with formatted relationship lists.
The expansion queries the graph to enumerate relationships for each item.
This pass is opt-in: only files whose resolved :traceability-links: attribute is true get macro expansion.
The attribute can be set per page in the document header, or globally via the component’s antora.yml or the playbook asciidoc.attributes.
Partials have the attribute forced on since they have no document attributes of their own.
Files without the attribute have the macro text stripped.
Display style (list, table, inline), sort order (target-id, target-title, relation-type), and collapsible sections are configurable via document attributes.
Pass 2b: Expand graph macros (pages and partials)
tracer:graph[], tracer:graph-coverage[], and tracer:config-graph[] placeholders are replaced with rendered diagram image references.
The graph’s toDot() and toVegaLite() methods generate the item and coverage diagram source; toConfigDot() renders the configuration model.
This pass is opt-in via :traceability-graph: true.
It’s independent of Pass 2 — you can enable graph macros without link macros, and vice versa.
Pass 3: Substitute relationship links (pages and partials)
Inline relationship macros (``) are replaced with clickable xrefs to the target item’s source page. This is the mechanism that makes matrix cells clickable links to item definitions.
For page items: xref:page.html#ID.
For partial items: link:URL#ID (since partials don’t produce HTML pages).
This pass also runs injectTitleIds() (prepends ID to title for visible display) and unindentItemMacros().
Artifact registration (per component version)
After Pass 3, the extension registers generated artifacts in the content catalog for each component version, under every module with AsciiDoc content.
Matrices (HTML, CSV, JSON) are generated from the working graph.
A traceability/graph.json snapshot serializes the same version’s working graph in the canonical { format, component, version, items, relationships } shape, excluding pubUrl.
Both are gated by the generateMatrices option and refresh a committed copy in place rather than adding a duplicate.
Graph lifecycle
The extension maintains two graphs with distinct lifecycles:
-
Working graph — cleared and rebuilt per version during
contentClassified(spanning components at the same version), so xrefs stay version-scoped (REQ-249). -
Full graph — accumulated across all components and versions by merging each version’s working graph; the
sitePublishedgeneration passes (matrices, coverage, overview) read this graph.
The working graph’s lifecycle:
-
Creation: The extension initializes both graphs synchronously in its constructor. Config/preset loading is a synchronous file read.
-
Population (Pass 1, per version): The working graph is cleared, then items and relationships are added for that version. Validation runs on each
addRelationship()call. After each version, the working graph is merged into the full graph. -
Quiescent (between Pass 1 and Pass 2, within a version): The working graph is complete for that version. Queries and macro expansion are safe.
-
Macro expansion (Pass 2/2b/3): The working graph is read-only. Macros query it to generate output. No new items or relationships are added.
-
Finalization: The standalone output directory (matrices, coverage, overview) is written at
sitePublishedfrom the full graph, so it spans every component and version. Thegraph.jsonsnapshot is not part of this pass — it is registered duringcontentClassifiedfrom the working graph, so each published version keeps its own copy.
Component interaction
The extension follows a dependency-injection pattern:
-
ConfigLoaderis created first (config/preset loading) -
TraceabilityGraphreceivesConfigLoaderfor validation -
DocumentParserreceivesConfigLoaderfor role validation -
MatrixGeneratorreceives bothTraceabilityGraphandConfigLoader -
Neo4jExporterreceivesTraceabilityGraph
Each component is independently testable without mocking frameworks. There is no service locator, DI container, or global state.
File state caching
The prepareFile() method computes file content, document attributes, and item block positions once per file, then feeds the same PreparedFile to all macro-expansion methods.
This eliminates redundant buffer conversions and regex scans.
Related
-
API Reference — component interfaces
-
Architecture — arc42 architecture with sequence and pass-pipeline diagrams
-
Architecture Decisions — design rationale behind these patterns