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

Installation

MarkSpec ships as a single self-contained binary. There are three ways to get started, ordered by the experience they give you:

The MarkSpec extension (driftsys.markspec-ide) bundles the language server directly. Install it and you get real-time diagnostics, completions, and go-to-definition with zero extra configuration.

Install from the marketplace:

  1. Open VS Code.
  2. Open the Extensions panel (Ctrl+Shift+X / Cmd+Shift+X).
  3. Search for MarkSpec.
  4. Click Install on the driftsys.markspec-ide extension.

The extension prompts you to install or locate the markspec binary the first time it activates. Click Download to let it fetch the platform binary automatically, or point it at an existing install.

See VS Code extension for the full feature list, settings reference, and troubleshooting.

Binary install

A self-contained binary without any runtime requirement.

macOS / Linux (install script)

curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh

The script:

  1. Detects your platform and architecture.
  2. Downloads the release binary from GitHub Releases.
  3. Verifies the SHA256 checksum.
  4. Places the binary in ~/.local/bin.

Add ~/.local/bin to your PATH if it is not already there:

# bash / zsh
export PATH="$HOME/.local/bin:$PATH"

Manual download

Pre-built binaries are attached to every GitHub Release. Download the archive for your platform, extract the markspec binary, and place it anywhere on your PATH.

PlatformFile
macOS (Apple)markspec-macos-aarch64.tar.gz
macOS (Intel)markspec-macos-x86_64.tar.gz
Linux (x86_64)markspec-linux-x86_64.tar.gz
Linux (aarch64)markspec-linux-aarch64.tar.gz
Windows (x86_64)markspec-windows-x86_64.zip

Deno (from source)

If you already use Deno and prefer running from source:

deno install -g jsr:@driftsys/markspec

Binary is placed in ~/.deno/bin. Run without installing:

deno run jsr:@driftsys/markspec --help

Verify

markspec --version
# markspec 0.5.0 (core-schema 1)

AI assistant skillset (upskill)

MarkSpec ships a Claude Code skillset that teaches AI assistants the MarkSpec authoring conventions — entry block syntax, EARS patterns, the agent write loop, and traceability review.

Install it with upskill:

upskill add markspec:markspec-core.bundle.md

This registers the following skills in your project’s .claude/plugins/:

SkillPurpose
markspec-entry-authoringEntry block syntax, shapes, attributes
markspec-core-rulesValidation rules and diagnostic codes
markspec-write-loopThe insert → format → validate agent loop
markspec-gherkinGWT / Gherkin pattern for test entries
markspec-traceability-reviewCross-file link review agent
markspec-profile-bundle-authoringWriting and publishing profile manifests

See AI agents and skillset for MCP server setup and how to use the skills from Claude Code or Claude Desktop.

Quickstart

Get MarkSpec running in 15 minutes. This chapter walks a single linear path from install to a rendered HTML book with entry blocks and a traceability link.

Step 1 — Install (2 min)

Downloads a self-contained binary — no Deno or Node.js required at runtime:

curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh

The installer detects your platform (macOS or Linux), downloads the release binary, verifies the SHA256 checksum, and places it in ~/.local/bin. Add that directory to your PATH if it is not already there.

Deno runtime (for Deno developers)

If you already have Deno installed and want to run MarkSpec from source:

deno install -g jsr:@driftsys/markspec

If markspec is not found after install, add ~/.deno/bin to your PATH.

Run without installing: deno run jsr:@driftsys/markspec --help

Verify the install

markspec --version

Step 2 — Create a project (1 min)

Coming soon: markspec init

A future release will scaffold a project with a single command. For now, create the files by hand — it takes 30 seconds.

Create a project directory and two configuration files:

mkdir my-project && cd my-project

Create project.yaml:

name: my-project
version: "1.0.0"

Create .markspec.yaml (activates the default profile):

profiles: []

That is enough to start. Both files are picked up automatically when you run any MarkSpec command from inside my-project/.

Step 3 — Author three entries (5 min)

Create requirements.md with a stakeholder requirement, a software requirement, and a traceability link connecting them:

# Requirements

- [STK_PRJ_0001] System availability

  The system shall be available 99.9% of the time.

- [SRS_PRJ_0001] Health check endpoint

  The service shall expose a `/health` endpoint returning `200 OK`.

      Satisfies: STK_PRJ_0001

An entry is a Markdown list item where:

  • The display ID in […] identifies the entry (STK_PRJ_0001).
  • The rest of the list-item text is the title (System availability).
  • The indented paragraph underneath is the body.
  • An indented code block at the end holds attributes (Satisfies:).

Step 4 — Format: stamp ULIDs (2 min)

Run markspec format to assign a permanent ULID to each entry:

markspec format requirements.md
info: requirements.md:3 assigned Id: 01KPVVC9J2B1ZA64QZEMHF02PW to STK_PRJ_0001
info: requirements.md:9 assigned Id: 01KPVVC9J2B1ZA64QZEMHF02PX to SRS_PRJ_0001
2 file(s) formatted, 0 unchanged (2 total)

Your file now has Id: attributes:

- [STK_PRJ_0001] System availability

  The system shall be available 99.9% of the time.

      Id: 01KPVVC9J2B1ZA64QZEMHF02PW

- [SRS_PRJ_0001] Health check endpoint

  The service shall expose a `/health` endpoint returning `200 OK`.

      Id: 01KPVVC9J2B1ZA64QZEMHF02PX
      Satisfies: STK_PRJ_0001

The ULID is a universally unique, immutable identifier. Once assigned it never changes — even if the display ID or title is renamed.

Step 5 — Validate: check traceability (2 min)

Run markspec validate to confirm the link is intact:

markspec validate requirements.md

No output means validation passed (exit code 0).

Now break a reference to see a failure:

# temporarily change "Satisfies: STK_PRJ_0001" → "Satisfies: STK_PRJ_9999"
markspec validate requirements.md
error[MSL-R080]: requirements.md:13 unresolved reference: STK_PRJ_9999

Restore the correct reference before continuing.

Step 6 — Build: render an HTML book (2 min)

Create a minimal SUMMARY.md:

# Summary

- [Requirements](requirements.md)

Build the site:

markspec book build
wrote _site/requirements.html
wrote _site/index.html

Step 7 — View the output (1 min)

Open _site/index.html in a browser. The entry blocks render with type-based styling, display IDs, and clickable traceability links.

Editor integration

The MarkSpec VS Code extension (editors/vscode/ in the repository) provides real-time diagnostics, entry block completion, and go-to-definition — all backed by the same binary you just installed. See CLI guide — Editor and LSP integration for setup instructions covering VS Code, Neovim, and other editors.

Next steps

I want to…Go to
Understand entries, shapes, and typesConcepts
See all CLI flags and subcommandsCLI guide
Set up a compliance profileProfile guide
Browse answers to common questionsFAQ

Concepts

Content coming in a future release.

This chapter will cover the MarkSpec mental model: entries, shapes (Authored and Reference), types, traceability links, profiles, and listing directives. It is the skimmable reference all three audiences (architect, developer, compliance lead) read before diving into their audience-specific chapter.

This chapter will explain:

  • Entries — the fundamental unit: a display ID, a title, a body, and a trailer block of attributes.
  • Shapes — Authored entries (identified by a ULID stamped by markspec format) versus Reference entries (citations to external artefacts such as standards or upstream requirements).
  • Types — the vocabulary layer declared by profiles; each type carries a display-ID pattern, allowed attributes, and traceability rules.
  • Traceability links — directed edges in the requirement graph, created by trace attributes (Satisfies:, Derived-from:, Verified-by:, etc.).
  • Profiles — layered manifests that declare type vocabulary, attribute rules, and display-ID patterns for a project or compliance standard.
  • Listing directivesglossary, components, and references directives that generate structured listing documents from the entry graph.

Until this chapter ships, the canonical source is docs/spec/internal/markspec-core-data-model.md (§1 — the two-layer model and type system).

Authoring guide

Content coming in a future release.

This chapter is the primary resource for architects who structure a project’s requirements and architecture documentation. Its content depends on listing-directive support and profile-schema implementation, which are not yet shipped on main.

This chapter will cover:

  • Project layout — recommended directory structure for multi-domain requirements documents.
  • Entry authoring workflow — write, format, validate, review; the canonical insert → format → validate loop for AI agents and human authors.
  • Traceability graph design — how to structure Satisfies: chains across stakeholder requirements, software requirements, and test entries.
  • Listing documents — authoring glossary.md, components.md, and references.md using the listing directives.
  • Profile-driven type vocabulary — declaring and using project-specific entry types.
  • In-code specs — embedding MarkSpec entry blocks in Rust /// and Kotlin /** doc comments; the V-model colocated test convention.

Until this chapter ships, refer to AGENTS.md (§Test conventions — V-model) for the in-code spec pattern, and the CLI guide for the authoring commands.

Profile guide

Full profile guide coming in a future release.

This chapter is the primary resource for compliance leads who stack profiles to map a project to ASPICE, ISO 26262, or other standards. Its detailed content depends on the profile-schema implementation, which is in progress.

This chapter will cover profile stacking, ASPICE mapping, and ISO 26262 alignment. The current section documents how to bind a profile to a project — the configuration that is already shipped.


.markspec.yaml

The .markspec.yaml file binds a project to one or more profile manifests. It sits next to project.yaml in the project root.

Example

profiles:
  - ./profiles/markspec.yaml

Profile specifiers

Three formats are supported:

Local path — relative to .markspec.yaml:

profiles:
  - ./profiles/markspec.yaml
  - ../shared/markspec.yaml

Git HTTPS — cloned and cached locally:

profiles:
  - git+https://github.com/acme/compliance-profiles.git#v1.0.0

Git HTTPS with subpath — for monorepos:

profiles:
  - git+https://github.com/acme/profiles.git/aspice#v2.0.0

Git file — for local development:

profiles:
  - git+file:///home/user/profiles.git#main

Only one profile is supported in the current implementation. If multiple profiles are listed, a PROFILE-LOAD-006 warning is emitted and only the first is used.


Profile manifests

A profile manifest (markspec.yaml) declares the vocabulary for a project: entry types, attributes, traceability rules, and display-ID patterns.

Profiles can extend other profiles via extends:, forming a chain. Child profiles can add types and attributes, or tighten constraints (e.g., narrowing cardinality, adding required: true) — but cannot relax them.

Use markspec profile show to inspect the active profile chain and markspec doctor for a project health check.

Scaffold a new profile with:

markspec profile new my-profile

This creates a my-profile/ directory with a markspec.yaml manifest and a README.md.


Coming in a future release

  • ASPICE / ISO 26262 mapping — how profile types correspond to process work products and safety integrity levels.
  • Profile stacking — composing a project profile on top of a compliance base profile.
  • Type vocabulary reference — all built-in types, their allowed attributes, and their traceability constraints.
  • Publishing profiles — validating a profile for distribution with markspec profile publish.

CLI guide

Reference for project.yaml, every subcommand, and editor / LSP integration.

MarkSpec follows the Command Line Interface Guidelines. Every command supports --help. Commands that produce structured output support --format json for machine-readable output to stdout (diagnostics always go to stderr).

Exit codes: 0 success, 1 error, 2 warnings only (validate).

Global options (available on every command):

FlagDescription
-h, --helpShow help
-V, --versionShow version
-q, --quietSuppress non-error output

Project configuration

project.yaml

Every MarkSpec project requires a project.yaml in the project root. MarkSpec discovers it by walking up from the current directory.

Minimal example

name: my-project
version: "1.0.0"

Complete example

name: io.acme.braking-system
version: "2.3.0"
labels:
  - ASIL-A
  - ASIL-B
  - ASIL-C
  - ASIL-D
parents:
  - https://acme.com/refhub
parent-fallback: https://driftsys.github.io/refhub

Fields

FieldTypeRequiredDefaultDescription
namestringyesProject name. Reverse-DNS convention recommended.
versionstringyes"0.0.0"Project version. Quote in YAML to avoid number coercion.
labelsstring[]no[]Allowed label vocabulary. Empty means no constraint.
parentsstring[]no[]Upstream parent registry URLs, searched in order.
parent-fallbackstringnohttps://driftsys.github.io/refhubFallback registry when parents don’t resolve a reference.

Directory conventions

MarkSpec does not enforce a directory layout. By convention:

  • docs/ — Markdown files containing requirements and design documentation
  • src/ — source code with doc-comment entries (Rust ///, Kotlin /**)
  • project.yaml — project root marker

The compile and report commands accept explicit paths or globs:

markspec compile "docs/**/*.md"
markspec compile docs/requirements.md src/main.rs

Profile configuration (.markspec.yaml and profile manifests) is covered in the Profile guide.


Commands

Authoring

format

Stamp ULIDs, fix indentation, normalize attributes.

markspec format <file...>
markspec format --check <file...>
FlagTypeDefaultDescription
--checkboolfalseReport changes without writing. Exit 1 if changes needed

Examples:

# Format a single file (writes changes in place)
markspec format docs/requirements.md

# Format multiple files
markspec format docs/*.md

# Check mode for CI — reports but doesn't modify
markspec format --check docs/*.md

validate

Check broken refs, missing Ids, malformed entries, duplicates.

markspec validate <file...>
FlagTypeDefaultDescription
--strictboolfalsePromote warnings to errors
--formatstringtextOutput format: json, text

Examples:

# Validate a file
markspec validate docs/requirements.md

# Strict mode — warnings become errors (useful for CI)
markspec validate --strict docs/requirements.md

# JSON output for tool integration
markspec validate --format json docs/*.md

Querying

show

Show details of a single entry by display ID or ULID.

markspec show <id> <paths...>
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text

Examples:

markspec show STK_PRJ_0001 "docs/**/*.md"
markspec show --format json STK_PRJ_0001 docs/requirements.md

context

Walk the Satisfies chain upward from an entry to see what it ultimately satisfies.

markspec context <id> <paths...>
FlagTypeDefaultDescription
--depthnumber10Maximum depth to walk
--formatstringtextOutput format: json, text

Examples:

markspec context SRS_PRJ_0001 "docs/**/*.md"
markspec context --depth 3 SRS_PRJ_0001 docs/requirements.md

dependents

List all entries that depend on (satisfy) a given entry.

markspec dependents <id> <paths...>
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text

Examples:

markspec dependents STK_PRJ_0001 "docs/**/*.md"

Building

compile

Parse files, build traceability graph, output compiled result.

markspec compile <paths...>
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text

Examples:

# Summary output
markspec compile "docs/**/*.md"

# Full JSON output for downstream tools
markspec compile --format json "docs/**/*.md" > compiled.json

report

Generate a traceability matrix or coverage report.

markspec report <kind> <paths...>

kind is one of: traceability, coverage.

FlagTypeDefaultDescription
--formatstringmdOutput format: md, json, csv
--scopestringFilter by domain abbreviation
--labelstringFilter by label value
--outputstringWrite to file instead of stdout

Examples:

# Traceability matrix in Markdown
markspec report traceability "docs/**/*.md"

# Coverage report as CSV
markspec report coverage --format csv "docs/**/*.md"

# Write to file
markspec report traceability --output matrix.md "docs/**/*.md"

# Filter by label
markspec report traceability --label ASIL-B "docs/**/*.md"

export

Emit the compiled traceability graph in a portable format.

markspec export <format> <paths...>

Supported formats: json, yaml, csv.

Examples:

# JSON export
markspec export json "docs/**/*.md" > compiled.json

# CSV — one row per entry with display ID, title, type, shape, id, file, line
markspec export csv "docs/**/*.md" > entries.csv

# YAML
markspec export yaml "docs/**/*.md"

insert

Append a scaffolded entry block to a file. This is the agent write path — use it from scripts or AI agents rather than hand-authoring new blocks.

markspec insert <type> <file>
FlagTypeDefaultDescription
--printboolfalseEcho the inserted block to stdout as well

The command:

  1. Finds the highest existing display ID for <type> in <file>.
  2. Computes the next sequential display ID.
  3. Generates a fresh ULID.
  4. Appends the block with a blank separator.

Example:

markspec insert requirement docs/requirements.md --print
# → appends SRS_PRJ_0003, prints block to stdout

Follow with markspec format to normalize indentation and markspec validate to confirm no broken references.

create

Print a scaffolded entry block without writing it to any file.

markspec create <type> <paths...>

Example:

markspec create requirement "docs/**/*.md"

next-id

Print the next available display ID for a profile-declared type without creating an entry.

markspec next-id <type> <paths...>
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text

Example:

markspec next-id requirement "docs/**/*.md"
# → SRS_PRJ_0004

markspec next-id requirement "docs/**/*.md" --format json
# → {"type":"requirement","displayId":"SRS_PRJ_0004"}

lint

Run prose-quality analysis on entries (INCOSE lexicon, modal keywords, structural checks). Returns MSL-Q and MSL-M codes.

markspec lint <paths...>
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text
--strictboolfalsePromote warnings to errors

Lint does not run as part of validate or the pre-commit hook. It is a review-time quality gate.

Examples:

markspec lint docs/requirements.md
markspec lint --strict "docs/**/*.md"
markspec lint --format json "docs/**/*.md"

hook

Run format --check and validate on a list of files. Designed as a pre-commit hook entry point.

markspec hook [files...]

Exits 0 when no files are given (nothing staged). Exits 1 if any file needs formatting or has validation errors.

See Pre-commit hook for setup instructions.

Documents

doc build

Generate a single-document PDF via Typst.

markspec doc build <file>
FlagTypeDefaultDescription
-o, --outputstring<file>.pdfOutput file path

Examples:

markspec doc build docs/spec.md
markspec doc build -o output/spec.pdf docs/spec.md

Books

book build

Generate a multi-chapter static HTML site from a SUMMARY.md.

markspec book build
FlagTypeDefaultDescription
-o, --outputstring_siteOutput directory
-s, --summarystringSUMMARY.mdSUMMARY.md path

Examples:

markspec book build
markspec book build -o dist -s docs/SUMMARY.md

Profile and diagnostics

profile show

Show the active profile chain and effective configuration.

markspec profile show
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text

profile new

Scaffold a new profile directory with a starter manifest.

markspec profile new <id>

<id> must be lowercase alphanumeric with hyphens, optionally scoped (@org/name).

markspec profile new my-profile
markspec profile new @acme/iso26262

Creates <id>/markspec.yaml and <id>/README.md.

profile add

Add a profile to the project’s .markspec.yaml.

markspec profile add <spec>

<spec> is a local path or a scoped package name.

markspec profile add ./profiles/my-profile
markspec profile add @driftsys/iso26262

profile publish

Validate a profile manifest for publishability and print any issues.

markspec profile publish [--dir <dir>] [--format json|text]

Checks required fields (id, version), warns about missing description and license, and reports any manifest validation errors. Exits 0 when the manifest is publish-ready.

profile describe

Show full details for a profile element (type, attribute, relation, label, or convention).

markspec profile describe <kind> <name>

kind is one of: type, attribute, relation, label, convention.

markspec profile describe type requirement
markspec profile describe attribute ASIL
markspec profile describe relation Satisfies

doctor

Project health check: verifies project.yaml, profile configuration, and project structure.

markspec doctor
FlagTypeDefaultDescription
--formatstringtextOutput format: json, text

AI agent integration

mcp

Start the MarkSpec MCP server. Communicates over stdio JSON-RPC. Exposes the active project as MCP resources and tools to any MCP-capable AI client (Claude Code, Claude Desktop, GitHub Copilot in VS Code, OpenCode).

markspec mcp
Resources
  • markspec://profile — distilled profile manifest (types, attributes, link kinds, labels).
  • markspec://entries — index of all project entries, grouped by type.
  • markspec://entry/{displayId} — one entry per resource, with attributes, body, outgoing/incoming links.
Tools
  • entry_search { query, limit? } — rank-search entries by display ID and title.
  • entry_context { id, depth? } — walk the satisfies chain upward.
  • validate { files? } — run the validator, return a Markdown diagnostics report.
  • markspec_refresh — force-invalidate the compile cache (call after agent edits to guarantee freshness).
Claude Desktop config

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "markspec": {
      "command": "markspec",
      "args": ["mcp"],
      "cwd": "/path/to/your/markspec-project"
    }
  }
}

Restart Claude Desktop. The MarkSpec resources and tools appear in the attach menu.

Claude Code
claude mcp add markspec --command markspec --args mcp --cwd /path/to/project
VS Code (Copilot)

The markspec-ide extension auto-registers the MarkSpec MCP server with VS Code 1.101+ — install the extension and the server appears in Copilot’s MCP picker. See Editor and LSP integration — VS Code below.

For users who don’t run the extension, the manual recipe still works. Add a .vscode/mcp.json in your project:

{
  "servers": {
    "markspec": {
      "command": "markspec",
      "args": ["mcp"]
    }
  }
}

Copilot does not support MCP resource subscriptions today, but the markspec:// resources still work — the server runs a fresh staleness check on every read, so a re-read after an edit returns up-to-date content.

mcp install

Print MCP server configuration for a client.

markspec mcp install --client <client>

Supported clients: claude-desktop, cursor, vscode.

markspec mcp install --client claude-desktop
markspec mcp install --client cursor
markspec mcp install --client vscode

lsp install

Print LSP server configuration for an editor.

markspec lsp install --editor <editor>

Supported editors: vscode, neovim, zed.

markspec lsp install --editor neovim
markspec lsp install --editor zed

Not yet implemented

These commands are registered but print an error and exit:

CommandIntended purpose
book devLive preview with hot reload
deck buildSlides → PDF via Touying/Typst
deck devLive slide preview

Editor and LSP integration

MarkSpec ships a built-in Language Server Protocol (LSP) server. Run it with:

markspec lsp

The server communicates over stdio JSON-RPC — the standard transport that every LSP-capable editor supports.

Features

Diagnostics — broken references, missing IDs, duplicate display IDs, and malformed entries appear as inline errors and warnings as you type. File-local checks run immediately; cross-file validation runs on save.

Entry block completion — type - [ at the start of a line to get a pre-filled entry block scaffold with the next available display ID for each type defined in your profile.

ID reference completion — after a trace attribute keyword (Satisfies:, Derived-from:, Verified-by:, etc.) the server suggests all known display IDs in the project.

Source file context guard — in source files (Rust, Kotlin, Java, C, C++), completions only activate near entry markers or trace keywords. The server won’t interfere with your language’s native LSP (rust-analyzer, kotlin-lsp, etc.).

VS Code

Install the MarkSpec extension from the editors/vscode/ directory in this repository. The extension requires VS Code 1.101 or newer (the version that introduced the stable MCP extension API).

The extension provides two integrations in one install:

  • LSP — diagnostics, completions, and entry-block scaffolding in the editor.
  • MCP — registers a markspec MCP server with VS Code so Copilot (and any other MCP-aware client) can use the project’s resources and tools without a separate .vscode/mcp.json.

Both point at the same markspec binary, resolved from markspec.server.path.

From source

cd editors/vscode
npm install
npm run compile

Then in VS Code: Extensions → ⋯ → Install from VSIX or press Ctrl+Shift+PExtensions: Install from VSIX… and select the .vsix file, or use the development host:

code --extensionDevelopmentPath=editors/vscode

Configuration

SettingDefaultDescription
markspec.server.path"markspec"Path to the markspec binary (used by both LSP and MCP).
markspec.server.args["lsp"]Arguments passed to start the LSP server.
markspec.mcp.enabledtrueRegister the MarkSpec MCP server with VS Code.
markspec.mcp.args["mcp"]Arguments passed to start the MCP server.
markspec.trace.server"off"Trace level: off, messages, or verbose.

If markspec is not on your PATH, set the full path:

{
  "markspec.server.path": "/home/you/.local/bin/markspec"
}

MCP server

Once the extension is installed, the MarkSpec MCP server appears in Copilot’s MCP picker — no .vscode/mcp.json required. To disable the registration:

{
  "markspec.mcp.enabled": false
}

The manual .vscode/mcp.json recipe in mcp — VS Code (Copilot) remains supported for editors that don’t run the extension.

Neovim

Neovim’s built-in LSP client works out of the box. Add this to your init.lua:

vim.api.nvim_create_autocmd("FileType", {
  pattern = { "markdown" },
  callback = function()
    vim.lsp.start({
      name = "markspec",
      cmd = { "markspec", "lsp" },
      root_dir = vim.fs.root(0, { "project.yaml", ".git" }),
    })
  end,
})

For source files (Rust, Kotlin, etc.) where MarkSpec entry blocks appear in doc comments, add the relevant file types to the pattern list:

pattern = { "markdown", "rust", "kotlin", "java", "c", "cpp" },

The server’s context guard ensures it only activates near MarkSpec entry markers, so it won’t conflict with rust-analyzer or other language servers running on the same buffer.

With nvim-lspconfig

If you use nvim-lspconfig, add a custom server definition:

local lspconfig = require("lspconfig")
local configs = require("lspconfig.configs")

if not configs.markspec then
  configs.markspec = {
    default_config = {
      cmd = { "markspec", "lsp" },
      filetypes = { "markdown", "rust", "kotlin", "java", "c", "cpp" },
      root_dir = lspconfig.util.root_pattern("project.yaml", ".git"),
    },
  }
end

lspconfig.markspec.setup({})

Other editors

Any editor with LSP support can use markspec lsp. The server expects:

  • Transport: stdio (stdin/stdout JSON-RPC)
  • Trigger characters: [ (block scaffold) and : (ID reference)
  • Document sync: full text on each change

Point your editor’s LSP client at markspec lsp and it should work. If your editor needs a specific configuration example, please open an issue.

VS Code extension

The MarkSpec extension (driftsys.markspec-ide) provides first-class editor support for MarkSpec documents and source-file doc comments. It speaks the Language Server Protocol (LSP) and delegates to the same markspec binary you use on the command line.

Features

FeatureDescription
Real-time diagnosticsValidation errors and warnings inline as you type
Entry block completions- [ → full block scaffold with display ID and attribute skeleton
ID reference completionsSatisfies: → pick from all display IDs in the workspace
Type completionsType: → core types + profile-declared types
HoverHover any display ID to preview the entry’s title, type, and body
Go-to-definitionF12 on a display ID jumps to the entry’s source location
Find all referencesShift+F12 lists every file that references a display ID
Workspace renameF2 renames a display ID across the entire workspace
Document outlineOutline view lists every entry in the file
Workspace symbol searchCtrl+T fuzzy-searches entries by display ID or title
FoldingEach entry block is collapsible
Document highlightsCursor on a display ID highlights every occurrence in the file
Quick fixesOne-click fixes for MSL-M060 (uppercase modal), MSL-A030 (generated attr), and more

Install

  1. Open Extensions (Ctrl+Shift+X / Cmd+Shift+X).
  2. Search for MarkSpec.
  3. Install driftsys.markspec-ide.

The extension activates on any workspace that contains .md files.

Configuration

All settings live under the markspec. prefix in VS Code settings.

SettingDefaultDescription
markspec.server.path"markspec"Path to the markspec binary. Override if not on PATH.
markspec.server.args["lsp"]Arguments passed to the binary to start the LSP server.
markspec.trace.server"off"LSP protocol trace level: off, messages, or verbose.

Example — binary in a project-local path:

{
  "markspec.server.path": "${workspaceFolder}/.bin/markspec"
}

MCP server

The extension also registers MarkSpec as an MCP server so Claude and other AI agents can query your entry graph directly from inside the editor.

No extra configuration is required — the extension reads markspec.server.path and registers the MCP server automatically. In VS Code with Copilot or Claude extension enabled, the server appears as MarkSpec in the agent tool list.

Neovim / other LSP clients

Any editor that supports LSP can use markspec lsp. Example Neovim (lazy.nvim) configuration:

require("lspconfig").markspec.setup({
  cmd = { "markspec", "lsp" },
  filetypes = { "markdown" },
  root_dir = require("lspconfig.util").root_pattern("project.yaml"),
})

Generate the full configuration snippet for your editor:

markspec lsp install --editor neovim
markspec lsp install --editor zed
markspec lsp install --editor vscode   # prints JSON config block

Troubleshooting

Extension activates but shows no diagnostics

  • Confirm the binary is on PATH: markspec --version in a terminal.
  • Check the MarkSpec output panel (View → Output → MarkSpec) for LSP errors.
  • Verify the project has a project.yamlmarkspec validate requires one.

“markspec: command not found”

Set markspec.server.path to the absolute binary path, e.g. /home/user/.local/bin/markspec.

Completions not appearing

  • Completions for block scaffold require the line to start with - [.
  • Trace-attribute completions require the workspace to be indexed — check the output panel for “Indexed N files”.

AI agents and skillset

MarkSpec integrates with AI assistants at two levels:

  • MCP server — exposes the compiled entry graph to any MCP-capable agent so it can query entries, search by display ID, and read traceability context.
  • Skillset — teaches the agent MarkSpec’s authoring conventions so it can write and review entries correctly without constant guidance.

MCP server

The MCP server runs as a subcommand of the same markspec binary:

markspec mcp

It speaks the Model Context Protocol over stdio JSON-RPC and exposes the following tools:

ToolDescription
entry_searchFuzzy search entries by display ID or title
entry_contextWalk the Satisfies chain upward from an entry
markspec_refreshRe-index the workspace after file changes
validateRun validation and return structured diagnostics

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "markspec": {
      "command": "markspec",
      "args": ["mcp"]
    }
  }
}

Generate the snippet automatically:

markspec mcp install --client claude-desktop

Cursor

markspec mcp install --client cursor

VS Code (Copilot / Claude)

The VS Code extension registers the MCP server automatically — no extra configuration needed. See VS Code extension.

Skillset

The skillset is a bundle of Claude Code skills that teach AI assistants MarkSpec-specific authoring conventions. Install it once per project; the skills activate automatically when Claude Code opens the directory.

Install with upskill

Install into the current project (writes per-client output under .claude/, .github/, .opencode/, and .agents/ and records the install in .upskill-lock.json):

upskill add driftsys/markspec:skills/markspec-core.bundle.md

Install globally into $HOME instead, so the skills are available in every project for the current user:

upskill add --global driftsys/markspec:skills/markspec-core.bundle.md

The bundle includes:

SkillActivates onWhat it does
markspec-entry-authoringAny entry authoring requestGuides the agent through correct block syntax and attributes
markspec-core-rulesDiagnostic triageMaps MSL- codes to their fix, suppression, and rationale
markspec-write-loopFile modification tasksEnforces the insert → format → validate agent write loop
markspec-gherkinTest entry authoringApplies GWT / Gherkin structure to test entries
markspec-traceability-reviewPR reviews, traceability auditsWalks the graph, checks coverage, flags orphaned entries
markspec-profile-bundle-authoringProfile manifest writingValidates manifest fields, extends chains, display-ID patterns

Manual install (without upskill)

If your team does not use upskill, download the bundle from the repository and place it in .claude/plugins/:

curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/skills/markspec-core.bundle.md \
  -o .claude/plugins/markspec-core.bundle.md

Invoking skills from Claude Code

Skills activate automatically when the task matches. You can also invoke them explicitly:

/markspec-write-loop
/markspec-traceability-review

Agent write loop

The canonical pattern for AI-assisted requirement authoring is:

markspec insert <type> <file>    # scaffold a new entry
markspec format <file>           # assign ULID, normalize indentation
markspec validate <file>         # confirm no broken references

Each step produces structured output the agent can parse:

markspec insert requirement docs/requirements.md --print
markspec format docs/requirements.md
markspec validate docs/requirements.md --format json

The markspec-write-loop skill enforces this sequence so agents don’t skip the format or validate steps.

Pre-commit hook

markspec hook is a pre-commit hook that runs format-check and validation on the files staged for commit. Commits that contain malformed entries or broken references are rejected before they reach the repository.

What the hook checks

  1. Format — every staged .md file is checked against the canonical format (markspec format --check). If any file needs reformatting the commit is rejected with a message: needs formatting (run 'markspec format').
  2. Validation — cross-file rules run on all staged entries. Missing Id: attributes, broken Satisfies: references, and duplicate display IDs all block the commit.

The hook does not run markspec lint (prose analysis). That is a review-time quality gate, not a commit blocker.

Setup

Add to .pre-commit-config.yaml:

repos:
  - repo: local
    hooks:
      - id: markspec
        name: markspec
        entry: markspec hook
        language: system
        types: [markdown]
        pass_filenames: true

Install the hook:

pre-commit install

Plain Git hook

Create .git/hooks/pre-commit:

#!/usr/bin/env sh
set -e

# Get staged .md files
STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.md$' || true)

if [ -z "$STAGED" ]; then
  exit 0
fi

markspec hook $STAGED

Make it executable:

chmod +x .git/hooks/pre-commit

Sharing the hook with the team

Git hooks are not committed to the repository. Use one of these approaches to share them:

  • pre-commit framework (recommended) — the .pre-commit-config.yaml file is committed; developers run pre-commit install after cloning.

  • Bootstrap script — add a bootstrap script that installs the hook:

    #!/usr/bin/env sh
    cp scripts/pre-commit .git/hooks/pre-commit
    chmod +x .git/hooks/pre-commit
    

Bypass (emergency)

git commit --no-verify -m "emergency: skip markspec hook"

Use sparingly. Bypassed commits accumulate formatting debt that must be paid before the next regular commit.

CI traceability gate

Run MarkSpec in CI to enforce traceability and format hygiene across the entire repository on every push and pull request.

A minimal CI gate runs three jobs in sequence:

format-check → validate → (optional) lint

All three jobs consume no build artifacts — they operate on the committed source files only.

GitHub Actions

name: MarkSpec

on: [push, pull_request]

jobs:
  format-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install markspec
        run: curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
      - name: Format check
        run: markspec format --check docs/**/*.md

  validate:
    runs-on: ubuntu-latest
    needs: format-check
    steps:
      - uses: actions/checkout@v4
      - name: Install markspec
        run: curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
      - name: Validate
        run: markspec validate docs/**/*.md

  lint:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - uses: actions/checkout@v4
      - name: Install markspec
        run: curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
      - name: Prose lint
        run: markspec lint docs/**/*.md

GitLab CI

stages:
  - quality

markspec-format:
  stage: quality
  script:
    - curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
    - markspec format --check docs/**/*.md

markspec-validate:
  stage: quality
  script:
    - markspec validate docs/**/*.md
  needs: [markspec-format]

markspec-lint:
  stage: quality
  allow_failure: true   # lint is informational; remove to make it blocking
  script:
    - markspec lint docs/**/*.md
  needs: [markspec-validate]

Exit codes

CodeMeaning
0Clean — no errors, no warnings
1Errors present — commit should be blocked
2Warnings only — informational; gate at your discretion

The validate command exits 2 when only warnings are present. Use --strict to promote warnings to errors and make the gate fully binary:

markspec validate --strict docs/**/*.md

Traceability report as CI artifact

Generate a coverage or traceability matrix and upload it as an artifact:

- name: Traceability report
  run: markspec report traceability docs/**/*.md --output traceability.md

- uses: actions/upload-artifact@v4
  with:
    name: traceability
    path: traceability.md

Caching the binary

Cache ~/.local/bin/markspec between runs to avoid downloading on every job:

- uses: actions/cache@v4
  with:
    path: ~/.local/bin/markspec
    key: markspec-${{ runner.os }}-0.5.0

ISO 26262 / ASPICE workflow

A reference setup for automotive functional-safety and process-compliance projects. This recipe covers the entry type vocabulary, V-model traceability structure, and required label vocabulary.

Profile

Activate the bundled compliance profile in .markspec.yaml:

profiles:
  - "@driftsys/iso26262"

Or declare a local profile that extends it:

profiles:
  - "./profiles/my-project"

with profiles/my-project/markspec.yaml:

id: "my-project"
version: 0.1.0
extends: "@driftsys/iso26262"

Entry type vocabulary

The ISO 26262 profile declares the following entry types:

Display-ID prefixTypeExtendsLevel
STK_stakeholderRequirementAcceptance
SYS_system-requirementRequirementSystem
SRS_software-requirementRequirementSoftware
ARC_architectureContractSoftware
ICD_interfaceSoftwareInterfaceSoftware
TST_validation-testTestAcceptance
SIT_integration-testTestSystem
SWT_unit-testTestSoftware
HZD_hazardRiskSystem

V-model traceability structure

STK (stakeholder)
  └─ Satisfies ─→  SYS (system requirement)
                     └─ Satisfies ─→  SRS (software requirement)
                                        └─ Satisfies ─→  ARC / ICD

TST (validation)  ←─ Verifies ─  STK
SIT (integration) ←─ Verifies ─  SYS
SWT (unit test)   ←─ Verifies ─  SRS

The compiler generates inverse edges automatically — authors only write the forward Satisfies: and Verifies: attributes.

ASIL labels

Declare allowed ASIL labels in project.yaml to enable MSL-L010 enforcement:

name: io.acme.braking
version: "1.0.0"
labels:
  - ASIL-QM
  - ASIL-A
  - ASIL-B
  - ASIL-C
  - ASIL-D

Entries carry the label in the trailer block:

- [SRS_BRK_0107] Sensor debouncing

  The sensor driver shall debounce raw inputs to eliminate transient noise
  spikes of duration less than 10 ms.

      Id: 01HGW2Q8MNP3RSTVWXYZABCDEF
      Satisfies: SYS_BRK_0042
      Labels: ASIL-B

In-code entries (V-model colocated)

Software requirements and unit tests at the SRS/SWT level are colocated in source files using doc comments:

#![allow(unused)]
fn main() {
/// [SRS_BRK_0107] Sensor debouncing
///
/// The sensor driver shall debounce raw inputs to eliminate
/// transient noise spikes of duration less than 10 ms.
///
///     Id: 01HGW2Q8MNP3RSTVWXYZABCDEF
///     Satisfies: SYS_BRK_0042
///     Labels: ASIL-B
#[cfg(test)]
fn swt_brk_0107_debounce_rejects_short_pulse() {
    let result = debounce(5); // 5 ms pulse, below 10 ms threshold
    assert!(!result.passed);
}
}

Coverage report

markspec compile "docs/**/*.md" "src/**/*.rs"
markspec report coverage "docs/**/*.md" "src/**/*.rs"

The coverage report shows which requirements have at least one linked test and which are uncovered — the key metric for ISO 26262 traceability audits.

CI gate

Add to your pipeline (see CI traceability gate):

markspec validate --strict "docs/**/*.md" "src/**/*.rs"
markspec report coverage "docs/**/*.md" "src/**/*.rs" --output coverage.md

Flag the coverage report as a required CI artifact for the functional safety manager’s review.

FAQ

Short answers to the questions a team actually asks when trialing MarkSpec.


What is a display ID?

A display ID is the human-readable identifier in the […] bracket on an entry title line, for example STK_PRJ_0001. It encodes the entry type prefix (STK), an optional domain segment (PRJ), and a zero-padded sequence number (0001). Display IDs are mutable — you can rename STK_PRJ_0001 to STK_AEB_0001 — and MarkSpec rewrites all references in the workspace when you use the LSP rename command.


What is a ULID and why does MarkSpec assign one?

A ULID (Universally Unique Lexicographically Sortable Identifier) is a 26-character string like 01KPVVC9J2B1ZA64QZEMHF02PW. MarkSpec stamps one onto each entry during markspec format and stores it as the Id: attribute.

The ULID is immutable. Once assigned it never changes, even if the display ID or title is renamed. Traceability links between projects and external tools (JIRA, DOORS) reference the ULID, not the display ID, so renaming an entry never breaks cross-tool links.


How does MarkSpec discover project.yaml?

markspec walks up the directory tree from the current working directory until it finds a project.yaml file. The directory containing that file becomes the project root. If no project.yaml is found, commands that require project context (compile, show, context, dependents, report, next-id, doc build, book build) exit with an error. format and validate work without a project.yaml.


Can I use MarkSpec without a profile?

Yes. markspec format and markspec validate work without a profile — they apply built-in formatting rules and lint checks. Commands that need type vocabulary (compile, create, next-id) require a profile to know the display-ID patterns for each type.

To activate a profile, create a .markspec.yaml in the project root:

profiles:
  - ./profiles/markspec.yaml

See the Profile guide for the full configuration reference.


What do exit codes 0, 1, and 2 mean?

CodeMeaning
0Success — no errors, no warnings
1Error — validation failed or command error
2Warnings only — validate found warnings but no errors

In CI, treat exit code 2 as a pass or a fail depending on your policy. Pass --strict to markspec validate to promote warnings to errors (exit code 1).


Where should I put my Markdown files?

Anywhere you like — MarkSpec does not enforce a directory layout. The convention used in this project is:

  • docs/ — Markdown files containing requirements and architecture descriptions
  • src/ — source code with doc-comment entries (Rust ///, Kotlin /**)

Pass explicit paths or globs to commands that need them:

markspec validate docs/requirements.md
markspec compile "docs/**/*.md" src/main.rs

What is the difference between format and validate?

CommandWhat it doesWrites files?
markspec formatStamps ULIDs, normalizes indentation and attribute orderYes (in place)
markspec validateChecks broken references, missing IDs, duplicates, lint rulesNo

Run format first (or as a pre-commit hook via markspec hook) to ensure every entry has a ULID before committing. Run validate in CI to catch broken traceability links.


How do I set up the LSP in my editor?


Can I run multiple profiles at once?

The current implementation supports one active profile per project. If multiple profiles are listed in .markspec.yaml, a PROFILE-LOAD-006 warning is emitted and only the first is used. Profile stacking (extends chains) is the recommended way to compose vocabulary. See the Profile guide.

Migration guide

Stage 2 — content deferred.

This chapter will document how to migrate existing requirements documents, DOORS exports, and ReqIF files into MarkSpec format. Its content is deferred to Stage 2 of the MarkSpec documentation roadmap, after the toolchain distribution and profile-schema implementation are complete.

Per project decision: no migration tooling or backward-compatibility shims are provided until version 1.0. All migration paths will be documented here when they ship.

Stage 2 will cover:

  • DOORS XML → MarkSpec — automated export and ULID assignment.
  • ReqIF → MarkSpec — type mapping and attribute preservation.
  • Display-ID renaming — workspace-wide rename via the LSP or CLI.
  • Version upgrade notes — breaking changes between MarkSpec releases.