Skip to main content
Requirements rarely stand alone — a derived behavior depends on a base behavior, or a summary section re-uses the canonical wording from another node. xspec models these relationships as typed edges in the dependency graph using the d prop and the {text(...)} embedding expression. Both mechanisms share the same static-argument rule, which keeps the graph fully analyzable at parse time without executing any code.

Importing other spec files

Before you can reference a node in another file, you must import that file at the top of your spec:
Import rules:
  • The path must be relative — it must start with ./ or ../ and end in .xspec.
  • Only a single default binding is allowed per import. Named imports, namespace imports (* as), and side-effect imports are all errors.
  • You cannot bind the reserved identifiers S, Spec, or text.
  • No two imports in the same file may bind the same identifier.
  • Import cycles — where file A imports file B which (transitively) imports file A — are a build error.
Once imported, the binding (BASE in the example above) becomes the root of a property chain you can use in d props and {text(...)} expressions.

The d prop

The d prop on a <S> section declares that the current requirement depends on another. xspec records this as a depends edge in the graph, which coverage profiles and policy rules can query.

Local form

Reference a requirement in the same file by writing its ID as a string literal:

External form

Reference a requirement in another file using a property chain rooted at an imported binding:

Array form

Declare multiple dependencies by passing an array. The two reference forms mix freely:
  • Duplicate entries collapse silently — listing the same target twice is the same as listing it once.
  • d={[]} (empty array) is equivalent to omitting the d prop entirely.
  • Self-dependency (a node depending on itself) and ancestor-dependency (a node depending on one of its own ancestors) both produce cycle errors at build time.
The d prop requires a braced expressiond={BASE.auth.login} is correct. Writing d="BASE.auth.login" as a quoted string is a syntax error.

The {text(...)} embedding

{text(...)} splices the subtree text of another requirement node directly into the compiled Markdown output at that position. xspec also records an embeds edge in the graph so downstream tooling knows about the relationship.
When markdown.emit is enabled, each {text(...)} expression is replaced by the full subtree text of the referenced node — its own prose plus any descendant sections, in document order.

How {text(...)} affects hashing

An important detail: embedding a node does not incorporate the target’s content into the embedder’s own hash. If you edit BASE.auth.login, xspec updates BASE.auth.login’s hashes — but summary’s own hash only changes when summary’s own prose changes. This means impact analysis correctly attributes the change to its source, not to every node that embeds it.
Use {text(...)} when you want a living cross-reference: the compiled Markdown always reflects the current wording of the embedded requirement, and the graph records the relationship for coverage and policy checks.

The static-argument rule

Every reference — whether in a d prop or a {text(...)} expression — must be statically resolvable at parse time. xspec never executes your spec files, so it cannot follow runtime values. A valid reference is one of:
  • A plain string literal identifying a node in the same file: "auth.login"
  • A property chain rooted at an imported spec binding, using only dot access or string-literal bracket access: BASE.auth.login or BASE.auth["login"]
The following are all forbidden: Additionally, text(...) takes exactly one argument — passing zero or more than one argument is an error.

Dependency edge kinds

xspec distinguishes three edge kinds you’ll encounter in coverage profiles and policy rules: