Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MarkSpec — Markdown Flavor Specification

Introduction

MarkSpec is a Markdown flavor for traceable industrial documentation. It targets safety-critical and high-availability systems where requirements, traceability, and compliance documentation must live alongside code in version control.

MarkSpec is a three-layer stack:

  1. CommonMark — the parsing baseline.
  2. GFM / GLFM shared subset — platform extensions portable across GitHub and GitLab.
  3. MarkSpec extensions — requirement authoring, captions, inline references, directives, and book structure.

Source files are pure, readable Markdown. They render correctly on GitHub and GitLab without any build step. PDF generation, traceability matrices, and reference resolution are build concerns — not format concerns.

This specification is the normative reference for MarkSpec tooling.


Part 1 — Markdown Flavor

1.1 CommonMark

MarkSpec accepts all CommonMark syntax. The following features are supported without modification:

Headings:

# Document Title

## Section

### Subsection

Paragraphs and inline formatting:

The braking system shall achieve full braking force within 150ms of driver
input. The _debounce window_ is configurable. Use **bold** for emphasis and
`debounce_input()` for code.

Block quotes:

> The system shall meet all requirements specified in ISO 26262-6 §9.4 for
> software unit testing.

Lists:

- Pressure sensor
- Speed sensor
- Temperature sensor

1. Capture raw input
2. Apply debounce filter
3. Validate plausible range

Fenced code blocks (language required):

```rust
fn debounce_input(raw: u16) -> u16 {
    // implementation
}
```

Inline links:

See [ISO 26262-6](https://www.iso.org/standard/68388.html) for software-level
requirements.

Reference links:

See [ISO 26262-6] for software-level requirements.

[ISO 26262-6]: https://www.iso.org/standard/68388.html

Images (alt text required):

![Braking system architecture](diagrams/braking-overview.svg)

Hard line breaks (trailing \):

Id: SRS_01HGW2Q8MNP3\
Satisfies: SYS_BRK_0042\
Labels: ASIL-B

Horizontal rules:

---

HTML comments (used for directives):

<!-- markspec:ignore -->

MarkSpec restricts the following CommonMark features:

FeatureCommonMarkMarkSpec restriction
HeadingsATX and setextATX only.
Code blocksFenced (backtick and tilde) and indentedBacktick-fenced only.
Emphasis*text* and _text__text_ only.
Strong**text** and __text__**text** only.
List markers-, *, +- only.
Horizontal rules---, ***, ___--- only.
Hard line breaksTrailing \ and trailing double-spaceTrailing \ only.
Inline HTMLAny HTML elementComments only (<!-- -->). No HTML elements.
Front matterYAML --- blocks (not CommonMark)Not allowed.

MarkSpec requires beyond CommonMark minimums:

RequirementRule
First lineMust be an H1 heading.
H1 countExactly one H1 per file. Summary documents are exempt — additional H1s are part headings.
Heading levelsMust not skip (H2 → H4 is invalid).
Code fence languageRequired. Use text for plain output.
Image alt textRequired on every image.

1.2 GFM / GLFM shared subset

Only features supported by both GFM and GLFM are used. Platform-specific extensions are not part of the flavor.

Tables

Pipe syntax. Rows are exempt from line width limits.

| Sensor   | Min | Max  | Unit |
| -------- | --- | ---- | ---- |
| Pressure | 0   | 1023 | kPa  |
| Speed    | 0   | 255  | km/h |

Strikethrough

~~deprecated requirement~~

Task lists

- [x] Define sensor thresholds
- [ ] Validate against hardware spec
- [ ] Update traceability matrix

Footnotes

Supplementary context only — not for traceability.

The debounce window[^1] shall be configurable per sensor type.

[^1]: Debouncing eliminates transient electrical noise from raw sensor readings.

Syntax highlighting

Language identifier is required on all fenced code blocks.

```rust
fn debounce_input(raw: u16, window_ms: u32) -> u16 {
    // implementation
}
```

Math

Inline and block math expressions:

The response time is $t = 150\text{ms}$ under nominal conditions.

$$
d = v \cdot t
$$

Alerts

> [!NOTE]
> This requirement derives from ISO 26262-6 §9.4.

> [!WARNING]
> Failure to debounce may lead to spurious brake activation.

> [!CAUTION]
> **ASIL-B constraint** — changes require impact analysis.

Supported types: NOTE, TIP, IMPORTANT, WARNING, CAUTION. Custom title via bold text on the first line.

1.3 MarkSpec extensions

All extensions use valid CommonMark syntax — they render on GitHub and GitLab without tooling.

§1 Entry blocks

A list item starting with - [DISPLAY_ID] followed by a title on the same line, and indented body content on subsequent lines. The display ID is the entry’s human-readable identifier. The title is the rest of the first line after the closing ].

- [DISPLAY_ID] Title

  Body paragraphs.

  Key: Value\
  Key: Value

A - [DISPLAY_ID] with no indented body is a normal list item — not an entry block.

Example 1 — entry block:

- [SRS_BRK_0107] Sensor input debouncing

  The sensor driver shall debounce raw inputs to eliminate electrical noise
  before processing.

  The debounce window shall be configurable per sensor type.

  > [!WARNING]
  > Failure to debounce may lead to spurious activation.

  Id: SRS_01HGW2Q8MNP3\
  Satisfies: SYS_BRK_0042\
  Labels: ASIL-B

Example 2 — not an entry block:

- [See documentation] for details on configuration.

No indented body. Normal list item.

Emphasis (_text_) must not appear inside entry blocks. Strong (**text**) and inline code are allowed.

Part 2 defines the two families of entries (typed entries and reference entries), their ID formats, and their attributes.

Rendering of entry blocks (admonition-style left border, type coloring, label pills, cross-reference links) is specified in the Typography chapter, §“Entry rendering”.

§2 Attribute blocks

Key: Value lines at the end of an entry block. Separated by trailing \ except the last line.

Example 3 — attribute block:

Id: SRS_01HGW2Q8MNP3\
Satisfies: SYS_BRK_0042\
Labels: ASIL-B

Which attributes are valid depends on the entry type. Part 2 defines the builtin attributes.

Generated attributes (Verified-by, Implemented-by) are computed by tooling and never appear in source.

§3 Table captions

Emphasized paragraph starting with Table: immediately above a pipe table.

Example 4 — table with caption:

_Table: Sensor thresholds_

| Sensor   | Min | Max  |
| -------- | --- | ---- |
| Pressure | 0   | 1023 |

Slug: tbl.sensor-thresholds. Derived by stripping the Table: prefix, then applying the GFM anchor algorithm (lowercase, spaces to hyphens, punctuation stripped).

Example 5 — not a caption:

_This is just italic text._

| Column A | Column B |
| -------- | -------- |

Does not start with Table:.

§4 Figure captions

Emphasized paragraph starting with Figure: immediately below an image. Alternatively, the image alt text is the caption.

Example 6 — explicit caption:

![System overview](overview.svg)

_Figure: High-level architecture of the braking system_

Slug: fig.high-level-architecture-of-the-braking-system. Derived by stripping the Figure: prefix, then applying the GFM anchor algorithm.

Example 7 — alt text as caption:

![System overview](overview.svg)

Slug: fig.system-overview. Explicit caption takes precedence.

§5 In-code entries

Requirements can be authored in doc comments in source files. A doc comment starting with [TYPE_XYZ_NNNN] is recognized as a MarkSpec requirement. The leading - bullet is optional in doc comments — the [DISPLAY_ID] pattern alone is sufficient.

Example 8 — Rust doc comment requirement:

#![allow(unused)]
fn main() {
/// [SRS_BRK_0107] Sensor input debouncing
///
/// The sensor driver shall reject transient noise shorter
/// than the configured debounce window.
///
/// Id: SRS_01HGW2R9QLP4 \
/// Satisfies: SYS_BRK_0042 \
/// Labels: ASIL-B
#[test]
fn swt_brk_0107_debounce_filters_noise() {
    // test implementation
}
}

The doc comment is the requirement. The test function is the verification. The Verified-by link is implicit — tooling discovers that the test carrying this doc comment is the SWT.

Code annotations declare upstream links:

#![allow(unused)]
fn main() {
/// Verifies: SRS_BRK_0107
#[test]
fn swt_brk_0107_debounce_filters_noise() { ... }

/// Implements: SRS_BRK_0107
fn debounce_input(raw: u16) -> u16 { ... }
}

Tooling extracts doc comments and Verifies: / Implements: annotations to produce the same traceability output as Markdown-authored entries.


Part 2 — Builtin Types and Attributes

Part 1 defines the format — how to write entry blocks and attribute blocks. This part defines the vocabulary — the builtin types, their attributes, and their traceability rules.

2.1 Typed entries

An entry whose display ID matches TYPE_XYZ_NNN[N] (uppercase letters, underscore, 2–12 uppercase letters, underscore, zero-padded number of 3 or 4 digits starting from 001) is a typed entry. Typed entries are recognized in any MarkSpec file.

Typed entries have two identifiers:

  • Display ID — human-readable, in the [...] marker. TYPE is the entry type. XYZ is a 2–12 letter project or domain abbreviation. NNN[N] is zero-padded from 001 (3 digits) or 0001 (4 digits), unique within the project.
  • ULID — universally unique, in the Id: attribute. Formatted as TYPE_ULID (e.g., SRS_01HGW2Q8MNP3). The ULID ensures global uniqueness across projects and survives renumbering. Mandatory. Assigned by tooling, never hand-authored. Once assigned, it never changes.

Builtin types:

CategoryTypeFull name
RequirementsSTKStakeholder requirement
SYSSystem requirement
SRSSoftware requirement
ArchitectureSADArchitecture description
ICDInterface control document
VerificationVALAcceptance test
SITSystem integration test
SWTSoftware test

Non-builtin types are valid — tooling validates entry format but not traceability direction or level.

2.2 Typed entry attributes

AttributeRequiredFormat
IdyesTYPE_ULID
SatisfiesnoParent entry display ID(s)
Derived-fromnoReference ID + optional section locator
LabelsnoComma-separated tags

Derived-from format:

Derived-from: ISO-26262-6 §9.4

The ID before the space (ISO-26262-6) is validated against the registry chain. The section locator after it (§9.4) is free text — tooling warns on unknown sections when lists are available but does not error.

2.3 Reference entries

An entry whose display ID does not match TYPE_XYZ_NNNN is a reference entry. Reference IDs are slugs: letters, digits, and hyphens ([A-Za-z0-9-]+). Reference entries are recognized only in documents of type references.

ID conventions:

ISO-26262-6        ← ISO 26262-6:2018
ISO-IEC-15504      ← ISO/IEC 15504
DO-178C            ← RTCA DO-178C
ECSS-E-ST-40C      ← ECSS-E-ST-40C
SAE-J3061          ← SAE J3061
MISRA-C-2012       ← MISRA C:2012
AUTOSAR-R22-11     ← AUTOSAR R22-11

Example 9 — reference entries:

- [ISO-26262-6] ISO 26262 Part 6

  Road vehicles — Functional safety — Part 6: Product development at the
  software level.

  Document: ISO 26262-6:2018\
  URL: https://www.iso.org/standard/68383.html

- [DO-178C] DO-178C

  Software Considerations in Airborne Systems and Equipment Certification.

  Document: RTCA DO-178C\
  URL: https://www.rtca.org/products/do-178c/

Reference entry attributes:

AttributeRequiredFormat
DocumentnoFull document identifier
URLnoCanonical URL
Statusnoactive, withdrawn, superseded
Superseded-bynoReplacement entry ID
Derived-fromnoParent standard or regulation

Reference IDs are used in {{ref.ISO-26262-6}} inline references and in Derived-from: attributes on typed entries.

2.4 Requirement types

Three levels following the V-model hierarchy:

  • STK — stakeholder requirements.
  • SYS — system requirements.
  • SRS — software requirements.

Direction is upward: SRS → SYS → STK via Satisfies:.

2.5 Architecture types

SAD — architecture descriptions. Subtypes distinguished by attribute presence:

  • Decomposition — defines components. No extra attributes.
  • Allocation — maps requirements to components. Allocates: + Component:.
  • Constraint — defines architectural rules. Constrains:.

Example 10 — SAD decomposition:

- [SAD_BRK_0001] Braking system decomposition

  The braking subsystem consists of three components: sensor-driver, controller,
  and actuator-interface.

  Id: SAD_01HGW3A1BCD2\
  Satisfies: SYS_BRK_0001\
  Labels: ASIL-B

Example 11 — SAD allocation:

- [SAD_BRK_0010] Sensor debouncing allocation

  Sensor input debouncing is allocated to the braking ECU sensor-driver
  partition.

  Id: SAD_01HGW3A2EFG3\
  Allocates: SRS_BRK_0107\
  Component: BRK-ECU-SENSOR\
  Labels: ASIL-B

SAD-specific attributes:

AttributeFormat
AllocatesSRS display ID(s)
ComponentComponent name or registry entry ID
ConstrainsComponent name(s)

ICD — interface control documents. System-level contracts between deployment units, ECUs, or external systems.

Example 12 — ICD entry:

- [ICD_BRK_0001] Brake pressure CAN interface

  The braking ECU shall publish brake pressure on CAN message 0x142 at 10ms
  cycle time. Payload: 16-bit unsigned, 0.1 bar resolution, big-endian.

  Id: ICD_01HGW4A1BCD2\
  Between: braking-ecu, vehicle-dynamics-ecu\
  Satisfies: SYS_BRK_0035\
  Interface: {{ridl.brake-pressure-can}}\
  Labels: ASIL-B

ICD-specific attributes:

AttributeFormat
BetweenTwo parties, comma-separated
InterfaceOptional RIDL reference ({{ridl.id}})

SAD satisfies SYS. ICD satisfies SYS or SAD.

2.6 Verification types

Three test types mirror the requirement hierarchy:

RequirementTestFull name
STKVALAcceptance test
SYSSITSystem integration test
SRSSWTSoftware test

Each test level verifies its corresponding requirement level — cross-level verification is an error (MSL-T007).

ICD entries are verified by SIT — the test proves both sides implement the contract. A SIT entry can carry Verifies: ICD_BRK_0001 alongside Verifies: SYS_BRK_0042. No additional test type is needed.


Part 3 — Directives

Directives are HTML comments starting with markspec:. They are invisible on GitHub and GitLab. A markspec: token at the start of a line inside an HTML comment begins a directive. Everything until the next markspec: or --> is the payload.

3.1 Syntax

Example 13 — single directive:

<!-- markspec:glossary -->

Example 14 — multiple directives with multiline payload:

<!--
markspec:deck
markspec:deprecated Superseded by braking-v2.md which
  implements the revised sensor interface defined in
  SYS_BRK_0050.
-->

Continuation lines without markspec: are part of the previous directive’s payload.

Parsing rules:

  1. Scan each HTML comment for lines starting with markspec:.
  2. Token after markspec: is the directive name.
  3. Remainder of line is the start of the payload.
  4. Lines not starting with markspec: are payload continuation.
  5. A new markspec: line or --> terminates the payload.
  6. Range directives closed by <!-- markspec:end NAME -->.

3.2 Document directives

Placed in the first HTML comment after the H1 heading.

DirectivePayloadContext
markspec:glossarynonedoc
markspec:summarynonedoc
markspec:decknonedeck
markspec:referencesregistry URLboth
markspec:deprecatedreason (free text)both
markspec:paginatenonedeck

Type directives (glossary, summary, deck) are mutually exclusive. deprecated and references can coexist with a type directive. doc is the default — no directive for it. Multiple markspec:references directives declare multiple upstream registries. Order matters — registries are searched first to last, with an implicit fallback to RefHub.

glossary and summary are auto-detected from filename (GLOSSARY.md, SUMMARY.md). The directive is needed when the file has a different name (e.g., toc.md with markspec:summary). deck is never auto-detected — it always requires an explicit directive.

Example 15 — deck with pagination:

# Architecture Review

<!--
markspec:deck
markspec:paginate
-->

---

## System Boundaries

...

Example 16 — deprecated glossary:

# Legacy Terms

<!--
markspec:glossary
markspec:deprecated Replaced by platform-glossary.md as of
  v2.0.0.
-->

Example 17 — upstream registries:

# Braking Controller

<!--
markspec:references https://safety.company.io/registry
markspec:references https://driftsys.github.io/refhub
-->

Each markspec:references declares one upstream registry. Order matters — registries are searched first to last. RefHub is the implicit final fallback even if not declared.

3.3 Inline directives

Placed anywhere in the document body.

DirectivePayloadClosingContext
markspec:breakpage, columnboth
markspec:columnscount (2, 3)markspec:end columnsboth
markspec:sectionsection namedeck
markspec:notesfree textinside commentdeck
markspec:disableMSL rule ID(s)markspec:end disableboth
markspec:disable-next-lineMSL rule ID(s)both
markspec:ignorenonemarkspec:end ignoreboth

markspec:disable opens a range closed by markspec:end disable. markspec:disable-next-line suppresses rules for the next line only. markspec:ignore skips all MarkSpec processing — content inside the range is treated as plain Markdown with no requirement parsing, reference resolution, or MSL validation.

Example 18 — multi-column layout:

<!-- markspec:columns 2 -->

Content in the first column.

<!-- markspec:break column -->

Content in the second column.

<!-- markspec:end columns -->

In decks, markspec:break column works within a slide without the markspec:columns range — slide boundaries are the implicit region.

Example 19 — slide section:

---

<!-- markspec:section Architecture -->

## System Boundaries

A high-level view of the braking subsystem

---

## Component Design

...

The section name appears in slide footers until the next markspec:section.

Example 20 — speaker notes:

<!--
markspec:notes
Mention the 150ms response time requirement from
STK_BRK_0001. The debounce window was determined
by bench testing with the Bosch sensor module.
-->

Notes are entirely inside an HTML comment. The --> closes the payload — no markspec:end notes needed.

Example 21 — page break:

<!-- markspec:break page -->

Example 22 — lint suppression:

<!-- markspec:disable MSL-R011 -->

- [SRS_BRK_0108] Some _legacy_ requirement

<!-- markspec:end disable -->

<!-- markspec:disable-next-line MSL-R011 -->

- [SRS_BRK_0109] Another _legacy_ one

Part 4 — Book Structure

A MarkSpec book organizes MarkSpec files into a single navigable document. The book is defined by a source directory containing a SUMMARY.md at its root.

4.1 Layout

The book source directory contains the SUMMARY.md and all files it references. Directory structure is free-form — the SUMMARY.md defines the navigation, not the file tree.

Example 23 — book source directory:

src/
├── SUMMARY.md
├── GLOSSARY.md
├── overview.md
├── product/
│   ├── stakeholder-requirements.md
│   └── software-requirements/
│       ├── README.md
│       └── braking.md
├── architecture/
│   └── system-architecture.md
└── guide/
    └── getting-started.md

4.2 Summary

The summary is a SUMMARY.md file at the root of the book source directory. It is a manually authored table of contents — not a generated file tree. The author decides what appears and in what order.

The core structure is a nested list of links. Each link is a chapter. Nesting creates sub-chapters.

Example 24 — SUMMARY.md without parts:

# Braking Controller

- [Overview](overview.md)
- [Requirements](requirements.md)
- [Architecture](architecture.md)
- [Getting Started](getting-started.md)
- [Glossary](GLOSSARY.md)

Optional elements:

  • Unnested links — links outside a list. Front matter (before the first ---) and back matter (after the last ---). Rendered without numbering.
  • Part headings — H1 headings (# Part Name) label groups of chapters. Rendered as unclickable section dividers.
  • Separators--- separates front matter, body, and back matter.

Example 25 — SUMMARY.md with front matter, parts, back matter, and annexes:

# Braking Controller

[Overview](overview.md) [Introduction](introduction.md)

---

# Product

- [Stakeholder Requirements](product/stakeholder-requirements.md)
- [System Requirements](product/system-requirements.md)
- [Software Requirements](product/software-requirements/README.md)
  - [Braking](product/software-requirements/braking.md)
  - [Steering](product/software-requirements/steering.md)
  - [Diagnostics](product/software-requirements/diagnostics.md)

# Architecture

- [System Architecture](architecture/system-architecture.md)
- [Software Architecture](architecture/software-architecture.md)
- [Interface Contracts](architecture/interface-contracts.md)
- [Decisions](architecture/decisions/README.md)
  - [ADR-001: Documentation Format](architecture/decisions/adr-001.md)

# Guide

- [Getting Started](guide/getting-started.md)
- [Configuration](guide/configuration.md)
- [Troubleshooting](guide/troubleshooting.md)

# Verification

- [Traceability Matrix](verification/traceability-matrix.md)
- [Test Reports](verification/test-reports.md)

---

# Annexes

- [Color Palettes](annexes/color-palettes.md)
- [Coding Standards](annexes/coding-standards.md)

---

[Glossary](GLOSSARY.md) [Contributing](CONTRIBUTING.md)
[Changelog](CHANGELOG.md) [License](LICENSE.md)

The first H1 is the book title. Subsequent H1s are part headings. Front matter (unnested links before the first ---) introduces the book. Back matter (unnested links after the last ---) is reference and administrative content. Both render without numbering.

Rules:

  • The first H1 is the book title.
  • Summary documents are exempt from the single-H1 rule — additional H1s are part headings.
  • --- separates front matter, body, and back matter.
  • Every file referenced must exist.
  • Empty links (- [Title]()) are not allowed.
  • The summary is committed and human-authored — tooling may validate it but does not generate it.

4.3 Glossary

The glossary is a GLOSSARY.md file (or any file with a markspec:glossary directive). It uses heading-based structure.

Example 26 — glossary:

# Glossary

## A

### ASIL

Automotive Safety Integrity Level. Risk classification defined by [ISO 26262]
ranging from QM (quality managed, no safety relevance) to D (highest
criticality). The level is determined by the [HARA] process.

### ASPICE

Automotive SPICE. A process assessment model for the automotive industry derived
from [ISO/IEC 15504].

## H

### HARA

Hazard Analysis and Risk Assessment. Systematic process defined in [ISO 26262]
Part 3 for identifying hazards and assigning [ASIL] levels.

<!-- Internal references -->

[ASIL]: #asil
[ASPICE]: #aspice
[HARA]: #hara

<!-- External references -->

[ISO 26262]: https://www.iso.org/standard/68383.html
[ISO/IEC 15504]: https://www.iso.org/standard/60555.html

Structure rules:

  • H1 for the title.
  • H2 for letter groupings.
  • H3 for terms — alphabetically sorted within each group.
  • Link reference definitions at the end of the file — internal cross-links first, external references second.

Part 5 — Inline References

Inline references resolve content entities across all documents in the project. They use double braces: {{namespace.id}}.

5.1 Syntax

Example 27 — requirement and test references:

This module implements {{req.SRS_BRK_0107}}. Verified by {{test.SWT_BRK_0107}}.

Example 28 — reference to a standard:

Derived from {{ref.ISO-26262-6}}.

Example 29 — figure, table, and heading references:

See {{fig.system-overview}} and {{tbl.sensor-thresholds}}. Refer to
{{h.requirement-format}} for the full syntax.

On GitHub/GitLab: the braces render as plain text — the ID is human-readable. At build time: resolved to links.

5.2 Namespaces

NamespaceReferencesID source
reqRequirements (STK, SYS, SRS)Display ID
archArchitecture (SAD, ICD)Display ID
testTests (VAL, SIT, SWT)Display ID
refExternal referencesRegistry entry ID
figFiguresSlug from caption
tblTablesSlug from caption
hHeadingsGFM anchor

Slugs use the GFM algorithm: lowercase, spaces to hyphens, punctuation stripped.

5.3 Rules

  • Exactly two braces: {{ and }}.
  • The first period separates namespace from ID (e.g., {{ref.ISO-26262-6}} → namespace ref, ID ISO-26262-6).
  • No whitespace inside braces.
  • No sections ({{#}}), inverted sections ({{^}}), or partials ({{>}}).
  • Every reference must resolve at build time.
  • References are never committed in resolved form.
  • References inside fenced code blocks are not resolved — they render as literal text.

Part 6 — Document Model

6.1 Project properties

Shared across all documents. Every property always resolves. The project.yaml schema is defined at https://driftsys.github.io/schemas/project/v1.json.

PropertySourceFallback
projectproject.yamlnameRepo directory name
repositoryproject.yamlrepositorygit remote get-url origin
versionproject.yamlversiongit describe
licenseproject.yamllicenseproprietary

license: defaults to proprietary — absence of a declared license means all rights reserved. Use SPDX identifiers when specifying a license.

6.2 Document properties

Per-file. Nothing stored inside the document. Every property always resolves.

PropertySourceFallback
titleH1 headingFilename stem
typeFilename stem or directivedoc
revisionMerge-to-main count0
authorsproject.yamlGit unique commit authors
createdGit first commit timestampFile system creation time
modifiedGit last merge commit timestampFile system modification time
statusBranch/main/directiveapproved

revision: starts at 0. Increments on each merged PR/MR that modifies the file. Commits within a branch do not count.

authors: project.yaml recommended — Git history is fragile across moves and migrations.

status: draft (in branch), approved (on main), deprecated (markspec:deprecated directive).

6.3 Document types

TypeDetectionDescription
docdefaultAny Markdown file
glossaryGLOSSARY.md or directiveHeading-based term definitions
summarySUMMARY.md or directiveBook table of contents
referencesreferences.md or directiveExternal standard/regulation entries
deckdirective onlySlide deck (--- = slide breaks)
codefile extensionSource files with doc comments

Heading rules by type:

  • doc — one H1, no skipped levels.
  • glossary — one H1 (title), H2 (letter groups), H3 (terms).
  • summary — first H1 is the book title, additional H1s are part headings. Exempt from single-H1.
  • references — one H1, standard heading rules.
  • deck — one H1 (deck title). --- creates slide breaks. H2 headings start each slide. Heading hierarchy is per-slide — H3/H4 within a slide are valid regardless of other slides.

6.4 Content entities

EntitySourceID
reqSTK, SYS, SRS entry blocksDisplay ID
archSAD, ICD entry blocksDisplay ID
testVAL, SIT, SWT entry blocksDisplay ID
refReference entries, registry chainEntry ID
figFigure captions, alt textSlug
tblTable captionsSlug
hHeadingsGFM anchor

6.5 References

References (standards, regulations, external specifications) are resolved through a resolution chain. Projects declare upstream registries in project.yaml via the references field (project-wide) and per-file via markspec:references directives. Resolution order: local project → declared dependencies (in order) → declared references (in order) → per-file markspec:references directives → RefHub (implicit fallback).

{{ref.ID}} inline references and Derived-from: attribute values are validated against the resolution chain at build time.

6.6 Rule activation

Entry rules (MSL-R*) activate on any file containing - [DISPLAY_ID] entry blocks. Traceability rules (MSL-T*) activate on typed entries only. Glossary rules (MSL-G*) activate only on glossary documents. Summary rules (MSL-S*) activate only on summary documents.


Part 7 — Formatting Rules

Fixed rules:

RuleValue
Line endingslf
Emphasis_text_ (underscores)
Strong**text** (asterisks)
List marker- (dashes)
List indent2 spaces
Code fencesbackticks, language required
Trailing whitespaceremoved
Final newlinesingle \n
Table columnsaligned, padded (tables exempt from line width)
Horizontal rules---
Reference definitionsend of file, alphabetical within groups

Configurable rules (with defaults):

RuleDefaultOptions
Line width80any positive integer
Prose wrapalwaysalways, preserve

MarkSpec normalization

  • Attribute blocks — sorted to canonical order, trailing backslashes normalized.
  • Reference definitions — moved to end of file, sorted alphabetically within groups.
  • Alerts — markers uppercased, spacing normalized.
  • Front matter — stripped from all files.

Part 8 — Lint Rules

8.1 Severity

SeverityCI behavior
errorFails the build
warningReported, does not fail
noticeVerbose mode only

8.2 Entry format (MSL-R)

IDSeverityRule
MSL-R001errorEntry block: - [DISPLAY_ID] with indented body.
MSL-R002errorTyped entry: display ID matches [A-Z]{2,}_[A-Z]{2,12}_\d{3,4}.
MSL-R003errorTyped entry: Id: required, matches [A-Z]+_[0-9A-Z]{12,26}.
MSL-R004errorTyped entry: exactly one Id: per entry.
MSL-R005errorULID unique across repository.
MSL-R006errorDisplay ID unique within project.
MSL-R007errorDisplay ID type prefix matches ULID type prefix.
MSL-R008warningSequential numbering expected.
MSL-R009warningCanonical attribute order. Auto-fixed.
MSL-R010warningUnknown attributes. Generated attributes must not appear in source.
MSL-R011errorNo emphasis inside entry blocks.

MSL-R001 applies to all entry blocks. MSL-R002–R010 apply only to typed entries (ID matches TYPE_XYZ_NNNN). MSL-R011 applies to all entry blocks.

8.3 Traceability (MSL-T)

IDSeverityRule
MSL-T001errorSatisfies: target must exist.
MSL-T002errorDirection upward: SRS → SYS → STK. SAD → SYS. ICD → SYS or SAD.
MSL-T003warningSYS/SRS without Satisfies: — may be derived.
MSL-T004warningDerived-from: ID validated against registry chain.
MSL-T005error/// Verifies: target must exist.
MSL-T006error/// Implements: target must exist.
MSL-T007errorVAL→STK, SIT→SYS/ICD, SWT→SRS. Cross-level is error.
MSL-T008errorAllocates: target must be an SRS entry.
MSL-T009errorBetween: must list exactly two parties.

8.4 References (MSL-M)

IDSeverityRule
MSL-M001errorEvery {{namespace.id}} must resolve.
MSL-M002errorNamespace: req, arch, test, ref, fig, tbl, h.
MSL-M003errorNo sections, inverted sections, or partials.

8.5 Document structure (MSL-D)

IDSeverityRule
MSL-D001errorNo front matter. Auto-fixed.
MSL-D002warningFootnotes must not contain requirement IDs.
MSL-D003noticeNon-standard alert types.
MSL-D004warningCaption format: _Table:_ above, _Figure:_ below.
MSL-D005warningSVGs: viewBox required, no fixed width/height.
MSL-D006configurableInline links vs reference-style links. Controlled by referenceLinks config: none (no check), warn (prefer reference-style), enforce (require reference-style).
MSL-D007warningReference definitions at end of document, alphabetical within groups. Auto-fixed.

8.6 Glossary (MSL-G)

IDSeverityRule
MSL-G001errorH1 title, H2 letter groups, H3 terms.
MSL-G002warningTerms sorted within letter groups.
MSL-G003warningLink references at end of file, alphabetical within groups (internal then external).
MSL-G004errorCross-links reference existing headings.

8.7 Summary (MSL-S)

IDSeverityRule
MSL-S001errorEvery link target must reference an existing file.
MSL-S002errorNo empty links (- [Title]()).
MSL-S003errorNo duplicate file paths.
MSL-S004warningMarkdown files in the source directory not referenced in summary.

Part 9 — Configuration

9.1 Schema

.markspec.yaml:

referenceLinks: warn # none | warn | enforce

.markspec.toml:

referenceLinks = "warn"
PropertyTypeDefaultValues
referenceLinksstring"warn"none, warn, enforce

All formatting rules are fixed. There is no formatter choice — dprint is the formatter.

9.2 CI

lint:
  steps:
    - name: Format check
      run: markspec fmt --check

    - name: Lint
      run: markspec lint

9.3 Editor integration

  • Format-on-save: dprint (dprint.vscode).
  • Diagnostics: markdownlint (David Anson) for generic rules; MarkSpec LSP (markspec-lsp) for MSL rules.

Annex A — Formatter Compatibility

dprint is the MarkSpec formatter. This table maps MarkSpec rules to dprint settings and equivalent Prettier settings for teams migrating from Prettier.

BehaviordprintPrettier (reference)
EmphasisemphasisKind: underscores_text_ (default)
StrongstrongKind: asterisks**text** (default)
ListsunorderedListKind: dashes- (default)
Table alignmentalignedaligned
Trailing whitespaceremovedremoved
Final newlineensuredensured
Line widthlineWidth: 80printWidth: 80
Prose wraptextWrap: "always"proseWrap: "always"
Line endingsnewLineKind: "lf"endOfLine: "lf"
Indent(global) 2tabWidth: 2

Prettier’s proseWrap defaults to "preserve". MarkSpec requires "always".


Annex B — dprint.json

{
  "$schema": "https://dprint.dev/schemas/v0.json",
  "incremental": true,
  "markdown": {
    "lineWidth": 80,
    "textWrap": "always",
    "newLineKind": "lf",
    "emphasisKind": "underscores",
    "strongKind": "asterisks",
    "unorderedListKind": "dashes"
  },
  "includes": ["**/*.md"],
  "excludes": [
    "**/node_modules",
    "**/target",
    "**/dist",
    "**/build",
    "CHANGELOG.md"
  ],
  "plugins": [
    "https://plugins.dprint.dev/markdown-0.20.0.wasm"
  ]
}

Annex C — .prettierrc (migration reference)

Prettier is not the MarkSpec formatter. This config is provided for teams migrating from Prettier to dprint, to verify equivalent output during transition.

{
  "$schema": "https://json.schemastore.org/prettierrc",
  "printWidth": 80,
  "proseWrap": "always",
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "lf",
  "embeddedLanguageFormatting": "auto",
  "overrides": [
    {
      "files": ["CHANGELOG.md"],
      "options": {
        "proseWrap": "preserve"
      }
    }
  ]
}

Annex D — .markdownlint.yaml (standalone)

heading-increment: true
heading-style: { style: atx }
no-missing-space-atx: true
no-multiple-space-atx: true
blanks-around-headings: true
heading-start-left: true
no-duplicate-heading: { siblings_only: true }
single-title: true # override to false for SUMMARY.md
no-trailing-punctuation: true
first-line-heading: true
ul-style: { style: dash }
list-indent: true
ul-indent: { indent: 2 }
ol-prefix: { style: ordered }
list-marker-space: true
blanks-around-lists: true
no-trailing-spaces: true
no-hard-tabs: true
no-multiple-blanks: true
single-trailing-newline: true
line-length:
  line_length: 80 # from .markspec.yaml lineWidth
  tables: false
  code_blocks: false
  headings: false
blanks-around-fences: true
fenced-code-language: true
code-block-style: { style: fenced }
code-fence-style: { style: backtick }
no-emphasis-as-heading: false
no-space-in-emphasis: true
emphasis-style: { style: underscore }
strong-style: { style: asterisk }
no-bare-urls: true
no-space-in-links: true
no-empty-links: true
no-alt-text: true
link-fragments: true
link-image-reference-definitions: true
link-image-style: true
no-inline-html: true
no-multiple-space-blockquote: true
no-blanks-blockquote: false
hr-style: { style: "---" }
required-headings: false
proper-names: false

Annex E — .markdownlint-dprint.yaml

Formatting rules disabled (→ dprint).

heading-increment: true
heading-style: { style: atx }
no-missing-space-atx: true
no-multiple-space-atx: true
blanks-around-headings: false # → dprint
heading-start-left: true
no-duplicate-heading: { siblings_only: true }
single-title: true # override to false for SUMMARY.md
no-trailing-punctuation: true
first-line-heading: true
ul-style: { style: dash }
list-indent: true
ul-indent: { indent: 2 }
ol-prefix: { style: ordered }
list-marker-space: true
blanks-around-lists: false # → dprint
no-trailing-spaces: true # safety net
no-hard-tabs: true
no-multiple-blanks: false # → dprint
single-trailing-newline: false # → dprint
line-length:
  line_length: 80 # from .markspec.yaml lineWidth
  tables: false
  code_blocks: false
  headings: false
blanks-around-fences: false # → dprint
fenced-code-language: true
code-block-style: { style: fenced }
code-fence-style: { style: backtick }
no-emphasis-as-heading: false
no-space-in-emphasis: true
emphasis-style: { style: underscore } # safety net
strong-style: { style: asterisk } # safety net
no-bare-urls: true
no-space-in-links: true
no-empty-links: true
no-alt-text: true
link-fragments: true
link-image-reference-definitions: true
link-image-style: true
no-inline-html: true
no-multiple-space-blockquote: true
no-blanks-blockquote: false
hr-style: { style: "---" }
required-headings: false
proper-names: false

Annex F — In-Code Entries by Language

A doc comment starting with [TYPE_XYZ_NNNN] (with or without a leading -) is recognized as a MarkSpec entry. The following examples show the same entry in each supported language.

Rust

#![allow(unused)]
fn main() {
/// [SRS_BRK_0107] Sensor input debouncing
///
/// The sensor driver shall reject transient noise shorter
/// than the configured debounce window.
///
/// Id: SRS_01HGW2R9QLP4 \
/// Satisfies: SYS_BRK_0042 \
/// Labels: ASIL-B
#[test]
fn swt_brk_0107_debounce_filters_noise() {
    // test implementation
}
}

Kotlin

/**
 * [SRS_BRK_0107] Sensor input debouncing
 *
 * The sensor driver shall reject transient noise shorter
 * than the configured debounce window.
 *
 * Id: SRS_01HGW2R9QLP4 \
 * Satisfies: SYS_BRK_0042 \
 * Labels: ASIL-B
 */
@Test
fun `swt_brk_0107 debounce filters noise`() {
    // test implementation
}

C++ (Doxygen)

/// [SRS_BRK_0107] Sensor input debouncing
///
/// The sensor driver shall reject transient noise shorter
/// than the configured debounce window.
///
/// Id: SRS_01HGW2R9QLP4 \
/// Satisfies: SYS_BRK_0042 \
/// Labels: ASIL-B
auto debounce_input(uint16_t raw) -> uint16_t;

C (Doxygen)

/**
 * [SRS_BRK_0107] Sensor input debouncing
 *
 * The sensor driver shall reject transient noise shorter
 * than the configured debounce window.
 *
 * Id: SRS_01HGW2R9QLP4 \
 * Satisfies: SYS_BRK_0042 \
 * Labels: ASIL-B
 */
void debounce_input(uint16_t* raw);

Java (JDK 23+)

/// [SRS_BRK_0107] Sensor input debouncing
///
/// The sensor driver shall reject transient noise shorter
/// than the configured debounce window.
///
/// Id: SRS_01HGW2R9QLP4 \
/// Satisfies: SYS_BRK_0042 \
/// Labels: ASIL-B
@Test
void swt_brk_0107_debounce_filters_noise() {
    // test implementation
}

Java (legacy Javadoc)

Trailing backslashes are omitted — they render as literal characters in Javadoc. Attributes are on consecutive lines.

/**
 * [SRS_BRK_0107] Sensor input debouncing
 *
 * The sensor driver shall reject transient noise shorter
 * than the configured debounce window.
 *
 * Id: SRS_01HGW2R9QLP4
 * Satisfies: SYS_BRK_0042
 * Labels: ASIL-B
 */
@Test
void swt_brk_0107_debounce_filters_noise() {
    // test implementation
}

Language support summary

LanguageDoc syntaxMarkdown native?
Rust///yes
Kotlin/** */ KDocyes
C++/// Doxygenyes (since 1.8)
C/** */ Doxygenyes (since 1.8)
Java 23+/// (JEP 467)yes
Java (legacy)/** */ Javadocno (HTML)