Skip to main content
Coverage in xspec is not a matter of labeling requirements as “done” — it is a graph reachability question. A requirement is covered when a path exists in the dependency graph from a boundary node (typically a test or implementation) to that requirement. This approach means your coverage report is always derived from the actual relationships between code and specs, not from manually maintained metadata.

Coverage Profiles

You define coverage rules as profiles in xspec.config.ts. Each profile specifies four things: A typical profile named tested might declare that every leaf requirement in your specs/ group must be reachable from a node in your test/ code group via any edge kind transitively.

Building the Required Set

Not every node in the target group ends up in the required set. xspec applies these filters in order:
  1. Leaves only — only leaf nodes (sections with no children) are required, unless you set targets: "all" in the profile.
  2. Exclude coverage="none" — sections marked <S id="..." coverage="none"> are excluded and reported as ignored.
  3. Exclude root nodes — implicit file root nodes are never required.
The result is the set of nodes your boundary must reach for the profile to pass.

Direct vs. Transitive Mode

direct mode requires a single edge from a boundary node directly to the requirement being covered. This is strict: an intermediary requirement does not pass coverage through to a deeper one. transitive mode allows a path of one or more permitted edges. A test can cover a high-level requirement, which in turn covers lower-level ones through depends or embeds edges — as long as every step in the path uses a permitted edge kind.
contains edges never grant coverage in either mode. Structural parent-child nesting is not treated as a semantic dependency.

Running Coverage

Run xspec coverage to see the full report for all profiles:
Example output:
The report tells you:
  • required — total nodes that must be covered
  • covered — nodes with a valid path, including the shortest path found
  • uncovered — your work list; these nodes need a covering boundary node
  • ignored — nodes excluded from the required set, with the reason (e.g. coverage="none", root node)

CI Gating with --check

Pass --check to make xspec coverage exit with code 1 if any required node is uncovered. This turns coverage into a hard CI gate:
Use the profile name as the first argument to check a specific profile. Omit it to check all profiles.
Add xspec coverage --check as a step in your CI pipeline alongside your test run. Because xspec refreshes graph data automatically, you do not need a separate build step before the check.