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:- 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, ortext. - 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.
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 thedprop 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 {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.
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.
The static-argument rule
Every reference — whether in ad 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.loginorBASE.auth["login"]
Additionally,
text(...) takes exactly one argument — passing zero or more than one argument is an error.