Processing Pipeline

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. Files from components named by the excludeComponents extension option are filtered out before this pass, so excluded components never enter the graph and receive no macro expansion or generated attachments.

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)

When :traceability-links: resolves to true, each item without an explicit relationship-rendering macro receives outgoing and incoming relationship lists. An explicit tracer:outgoing[], tracer:incoming[], or tracer:links[] controls rendering for that item. The attribute can be set in a page header or inherited from the component descriptor or playbook; the page header overrides component and playbook values for page items. Items defined in partials use the partial component’s attribute, independent of the including page’s header. The independent :traceability-link-policy: selector filters rendered relationship lists by relation type and endpoint conditions without changing the working graph. The including page’s resolved selector applies to its partial items; if no policy resolves in that page context, partial items use the defining component’s policy. When the resolved attribute is absent or false, explicit relationship-rendering macros are stripped and automatic links are not added.

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.

Inline relationship macros are replaced with clickable xrefs to the target item’s rendered page. This is the mechanism that makes relationship and matrix links navigate to item definitions.

For page items, links target the item’s page and explicit ID anchor. For items defined in partials, links target the page that includes the partial while preserving the partial as source provenance.

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) apply an optional matrix link display policy before computing cell contents and coverage. 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 sitePublished generation passes (matrices, coverage, overview) read this graph.

The working graph’s lifecycle:

  1. Creation: The extension initializes both graphs synchronously in its constructor. Config/preset loading is a synchronous file read.

  2. 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.

  3. Quiescent (between Pass 1 and Pass 2, within a version): The working graph is complete for that version. Queries and macro expansion are safe.

  4. 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.

  5. Finalization: The standalone output directory (matrices, coverage, overview) is written at sitePublished from the full graph, so it spans every component and version. The graph.json snapshot is not part of this pass — it is registered during contentClassified from the working graph, so each published version keeps its own copy.

Component interaction

The extension follows a dependency-injection pattern:

  • ConfigLoader is created first (config/preset loading)

  • TraceabilityGraph receives ConfigLoader for validation

  • DocumentParser receives ConfigLoader for role validation

  • MatrixGenerator receives both TraceabilityGraph and ConfigLoader

  • Neo4jExporter receives TraceabilityGraph

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.