The .ttrs document source format
A document source is prose with {{ … }} directives in it. The prose is copied through as written; each directive names something in a Transitrix model and is replaced by what it resolves to.
On this page
What a source file contains
One text file, in two parts: a YAML header, then the document body.
The body is ordinary prose. Everything outside {{ … }} is fixed text and is copied through verbatim; each {{ … }} is a directive that names something in the model and is replaced by what it resolves to.
---
document: Market Requirements Document # required — the name its readers use
kind: mrd # required — matches the filename's middle segment
template_id: product.mrd # required — named in the run record
template_version: "1.0" # required — named in the run record
canon: ../canon # optional — the model repository
---
Four header fields are required; canon: is not. A source that names no model object and no derived figure resolves standalone, with no repository configured at all — that is a legitimate input in its own right, and an implementation reading such a source reports the no-repository state rather than treating it as a failed lookup.
How the filename is read
<basename>.<kind>.ttrs e.g. product.mrd.ttrs
platform.srs.ttrs
gateway.sdd.ttrs
The middle segment is the document kind — never a notation. A kind is a layout over the model: which elements a document of that kind selects, and how they are arranged. It carries no content of its own, gets no directive language of its own, and a new kind never means a new language. The kinds specified today are mrd, srs, sdd and sds; the header's kind: must agree with the filename.
Each notation has exactly one canonical extension, and it is either *.<short-name>.transitrix.yaml or *.<short-name>.ttrs. Which one a given notation takes is a property of that notation. Where .ttrs applies it replaces the longer suffix in full — it is never appended to it, and no notation carries both.
.trs is one keystroke away and is a different, widely used format. A file ending .trs where a document source is expected is reported as that near-miss by name, not as an unknown file.
The directive language
Delimiters are {{ and }}. Whitespace just inside them is insignificant, so {{REQ-14}} and {{ REQ-14 }} are the same directive. \{{ renders a literal {{ and is the only escape in the language. No directive nests inside another, except that the two block forms below take a body.
Inline forms
{{ REQ-14 }}— inline reference. Substitutes the named object's display value.{{ REQ-14.parent.title }}— field path. Substitutes one field, walking through any middle segment that itself names an object. Traversal is capped at depth 3, and a longer path is a failure rather than a deeper walk.{{ .field }}— row reference. A field of the current row of the enclosingeachblock, and meaningful only inside one.{{ trace from = … to = … via = … }}— trace. Renders trace coverage between two element types along one relation; all three attributes are required.{{ view <path> [as = …] [fit = width|page|none] }}— derived figure. A view authored in an existing notation and rendered from the model.fitdefaults towidth.{{ figure <path> [caption = "…"] [as = …] }}— supplied figure. An asset embedded as it is; never generated.{{ figref <name> }}— figure cross-reference, by theasname of a figure declared earlier in the same document. Figures are numbered in document order, and a forward reference is a failure rather than a deferred lookup.
Block forms — there are exactly two
{{# each <TYPE> [where …] [order by <field>] }} … {{/ each }} repeats its body once per selected element, binding each in turn as the current row. The where clause is deliberately small: and only, no or and no parentheses; = and != only; a literal on the right-hand side, never another field. order by takes a single field, and is what makes a rendered document reproducible.
{{# instruct <slot-id> }} … {{/ instruct }} declares an instruction slot — a section a deterministic pass does not fill, carrying question: and sufficient: (both required) and an optional comma-separated inputs:. A slot body is opaque: a parser scans from the opening tag straight to the matching close and keeps everything between as raw text, so a {{ REQ-14 }} written inside a slot is instruction prose and is never resolved. Slot ids are lower-case letters, digits and hyphens, and must be unique within a document.
Identifiers
An id follows the canonical grammar — REQ-14, CAP-1, BUSINESS_SERVICE-3. A capability id embeds a diagram address whose dots belong to the id, so a parser splits the CAPABILITY-V / CAPABILITY-H prefix off before it reads a field path: {{ CAPABILITY-V1.2.3 }} is one id and no field path, while {{ CAPABILITY-V1.2.3.name }} is that same id with the field path name.
A worked fragment
The committed conformance fixture, in full — a header, fixed text, a reference, a field path, a two-step path, a derived figure with a cross-reference to it, and one instruction slot.
---
document: Market Requirements Document
kind: mrd
template_id: product.mrd
template_version: "1.0"
canon: canon
---
# {{ CAP-1.title }}
## Scope
This document covers {{ CAP-1.text }}
## Requirements
**{{ REQ-14 }}** — {{ REQ-14.text }}
Its parent capability is {{ REQ-14.parent.name }}.
{{ view diagrams/context.blocks.transitrix.yaml as = context fit = width }}
{{ figref context }} shows where the capability sits.
## Market
{{# instruct market-size }}
question: How large is the addressable market for this capability, and how fast is it growing?
inputs: CAP-1, REQ-14
sufficient: A market size with a currency and a year, a growth rate, and the method used to derive both.
{{/ instruct }}
The file is product.mrd.ttrs, and the output it must produce is frozen beside it as product.mrd.expected.md.
Reference states
Resolving a reference yields exactly one state. Besides ok — resolved, admitted, and inside its validity interval — the language names five, and an implementation must keep every state it reports distinct from every other.
- unresolved
⚑U— no object with that id exists. - not admitted
⚑A— the object exists, but its admission state is not active. - out of validity
⚑V— its validity interval does not cover the render date. - suspect
⚑S— the object resolves, but the link to it is under suspicion. - no repository configured — the document cites the model, and none is configured.
The first three are classified in that order: existence, then admission, then validity. Collapsing any two states into one is non-conformance rather than a simplification — you have no repository and your repository lacks this id are different problems with different fixes, and each state names a different thing for its reader to go and do.
A non-ok reference never renders as its bare value. The state that reads as correct text is the one a reader has no way to see.
⚑S is derived from commit history rather than read from a file, and is the one state an implementation may decline to compute. Declining is permitted; being silent about it is not — three outcomes have to stay distinguishable: suspect, checked and clean, and never checked. A document that renders with no ⚑S anywhere is making a claim, and an implementation that never ran the check has made no claim at all.
Render profiles
An implementation offers at least two profiles, both selectable, and names which one a given run used:
- strict — fails the run on every non-ok state it computes, by exit status and not only in prose.
- lenient — does not fail; renders each reference it can, every non-ok one carrying its flag, and reports every state it found.
Two rules bind the pair: the lenient profile detects exactly what the strict one fails on — not a subset — and neither profile renders a non-ok reference as its bare value. The names above are the roles rather than a required spelling.
An implementation may leave any construct of the language unimplemented, and admitting a subset is expected. What it states in its own documentation is both halves: what it admits, and what it recognises and declines. A recognised-but-unimplemented construct fails by that name, under a code distinct from the one used for unknown or malformed syntax — the two messages are this is not in the language and this is in the language and not in this tool, and they send an author to different places.
What exists today
- The normative specification — DIRECTIVE_LANGUAGE.md, version 1.0, stable, dated 2026-08-07. It is a conformance contract written to be built against, and it defines the language once for every source that uses it. Where it and any implementation disagree, the specification wins.
- The reference implementation — @transitrix/document-renderer, the parser and its deterministic first pass. It exists to make the specification checkable, and its README states which constructs it admits and which it defers.
- The conformance fixture — a complete source file and its frozen output, committed together. It is the artefact an independent implementation diffs its own output against, and it is never regenerated.
- The kinds specified today — 29-mrd.md, 30-srs.md, 31-sdd.md, each defining a layout rather than a language.