> ## Documentation Index
> Fetch the complete documentation index at: https://xspec.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Requirement Specs in MDX with xspec

> Learn to write xspec requirement documents in MDX: the <S> section tag, allowed constructs, nesting, Markdown compilation, and own vs subtree text.

Spec files are standard MDX documents with a `.mdx` extension, saved as valid UTF-8 with no byte-order mark. xspec defines a small, strict subset of MDX constructs you can use — anything outside that subset is a build error. This constraint keeps spec files machine-readable and ensures the dependency graph stays fully static.

## Allowed constructs

Inside a spec file you may use:

* **Spec imports** — `import` statements that pull in other `.xspec` files (covered in [Dependencies](/writing-specs/dependencies))
* **`<S>` / `<Spec>` sections** — the primary structural element (synonyms; `<S>` is preferred)
* **`{text(…)}` embeddings** — splices another requirement's text inline
* **MDX comments** — `{/* … */}` for editorial notes that won't appear in output
* **Ordinary Markdown** — headings, paragraphs, lists, tables, fenced code blocks, and so on

Everything else — arbitrary JSX elements, general `{expression}` interpolations, `export` statements — is forbidden and causes an exit-2 error.

## The `<S>` section tag

The `<S>` tag (or its alias `<Spec>`) marks a named requirement node. Each section becomes a node in the dependency graph, identified by its `id`.

```mdx theme={null}
<S id="login">
The product supports login via username and password.
</S>
```

A **self-closing** form reserves an ID as an empty leaf node without any prose:

```mdx theme={null}
<S id="todo" />
```

Use the self-closing form to stub out a requirement you plan to fill in later — the ID is immediately available as a dependency target.

### The implicit root node

Every spec file has an implicit root node identified by the file's path. Content and `<S>` sections written at the top level of the file are owned by that root. You never declare the root node explicitly.

### Nesting

Place `<S>` sections inside one another to model a requirement hierarchy. The nesting relationship is recorded as parent–child edges in the graph.

```mdx theme={null}
<S id="auth">
The product supports authentication.

  <S id="auth.login">
  Users can log in with a username and password.
  </S>

  <S id="auth.logout">
  Users can end their session explicitly.
  </S>
</S>
```

<Note>
  Every non-root section **must** have an `id` prop. Omitting `id` on a non-root `<S>` is a build error.
</Note>

## Props reference

xspec recognises exactly four props on `<S>`. Any unknown prop name is an error.

| Prop       | Syntax                                    | Purpose                                        |
| ---------- | ----------------------------------------- | ---------------------------------------------- |
| `id`       | Quoted string: `id="login"`               | Unique identifier for this node                |
| `d`        | Braced expression: `d={BASE.auth.login}`  | Declares a dependency edge                     |
| `coverage` | Quoted string: `coverage="none"`          | Controls whether the node is a coverage target |
| `tags`     | Quoted string: `tags="negative temporal"` | Whitespace-separated labels for filtering      |

**Syntax rules:**

* `id`, `coverage`, and `tags` accept **plain quoted strings only** — brace syntax (`id={"login"}`) is an error.
* `d` accepts a **braced expression only** — a quoted string is an error.
* Spread attributes are not allowed.
* Repeating the same prop on one element is an error.

## Markdown compilation

When `markdown.emit: true` is set in your config, xspec compiles each `NAME.mdx` spec to `NAME.md`. The compiled output is pure Markdown: no JSX, no xspec-specific syntax.

During compilation:

* Spec imports are removed entirely.
* `<S>` opening and closing tags are stripped; their content remains.
* MDX comments (`{/* … */}`) are removed.
* `{text(…)}` expressions are replaced by the expanded subtree text of the target node.
* Everything else is preserved byte-for-byte.
* Any line that becomes empty purely as a result of removals is dropped.

<Tip>
  Use MDX comments freely for notes to your team — they disappear completely from compiled Markdown and never appear in the dependency graph.
</Tip>

## Own text vs subtree text

xspec distinguishes two text views for every node:

* **Subtree text** — the node's full contribution to compiled Markdown: its own prose with all descendant sections interleaved in document order.
* **Own text** — the same content but with child section contributions excised, leaving only the prose directly written inside that node.

This distinction matters for hashing and `{text(…)}` embeddings. When you embed a node with `{text(BASE.auth.login)}`, you get that node's subtree text. When xspec computes whether a node's *own* content has changed, it uses own text so that editing a child doesn't change the parent's own hash.

## Complete example

The following file demonstrates every allowed construct together:

```mdx theme={null}
import BASE from "./BASE.xspec"

{/* Editorial note: keep the summary in sync with marketing copy. */}

<S id="overview" coverage="none" d={BASE.auth}>
The product summary, for context.

{text(BASE.auth.login)}
</S>

<S id="lockout" tags="negative temporal">
Five consecutive failed attempts lock the account for 15 minutes.

  <S id="lockout.reset" />
</S>
```

Breaking it down:

* The `import` brings `BASE` in scope so you can reference `BASE.auth` and `BASE.auth.login`.
* The MDX comment is editorial — it won't appear anywhere in the output.
* `overview` declares a dependency on `BASE.auth`, opts out of coverage targets, and embeds `BASE.auth.login`'s subtree text inline.
* `lockout` carries two tags and contains a self-closing child `lockout.reset` that stubs a future requirement.
