Validates Markdown endpoint documentation: structure, components, mapping tables, and local links. Never edits files or touches the network.
cargo install mdspec --lockedPrebuilt x86_64 Linux binaries are on GitHub Releases. The x86_64-unknown-linux-gnu archive needs glibc; the statically linked x86_64-unknown-linux-musl archive also runs on Alpine and other images without glibc.
The default headings are Russian (see mdspec.toml). For English docs, save this as mdspec.toml:
[labels]
endpoint_prefix = "Endpoint:"
path_prefix = "Path:"
logic = "Logic"
components = "Components"
mappings = "Mappings"
examples = "Examples"
references = "References"
description = "Description"
source = "Source"
target = "Target"
request = "Request"
response = "Response"Write a document:
## Endpoint: List items
Path: `GET /v1/items`
### Logic
1. Return the items.Check it:
mdspec check docs/ --config mdspec.tomlLabels can be in any language. Unset labels keep their defaults.
mdspec check docs/
mdspec check docs/a.md docs/b.md
mdspec check 'docs/*/service/*.md'
mdspec check 'docs/**/*.md' --exclude 'docs/**/generated/**'
mdspec check docs/ --strict --format json- Quote globs.
*stays within one folder,**crosses folders. --excludeis repeatable and always wins.- Folders are scanned for
.mdfiles, respecting.gitignoreand.ignore. --strictturns warnings into errors.--format jsonprints a report to stdout. Match onruleIDs, not messages.
Exit codes:
0clean, or warnings only1rule violations2bad arguments, config, or input (wins over1)
Only a file passed with --config is read. Unknown keys are errors. All keys and defaults: mdspec.toml.
document.require_endpoint = falseallows documents without the endpoint heading and path.document.allow_unknown_sections = falserejects sections not listed in[labels].document.require_examples = truerequires the examples section.- Every
## <endpoint_prefix> nameheading starts an endpoint that runs to the next H2. Section and component rules apply per endpoint. An optional H1 title and other H2 sections such as general rules are not checked. An H1 after the first H2 is reported as MDS005 and otherwise ignored. Link definitions go in each endpoint's references section or in one H2 references section at the end of the document. - Components come in two kinds. Internal components live in an endpoint's
### Componentssection and may only be linked from that endpoint. General components live in one## Componentssection after the last endpoint and before## References, or in a separate file with no endpoint, and every one must be linked at least once from an endpoint or another component. Only files in the samemdspec checkrun count. references.require_reference_style = falseallows inline local links.references.definitions_at_end = falseallows definitions anywhere.references.toc_allowed = trueaccepts a paragraph that is only[TOC]or[[_TOC_]]as a table of contents marker instead of an undefined reference.diagnostics.warnings_as_errors = trueturns warnings into errors, exactly like--strict.
Examples use the English labels from Quick start.
Correct:
## Endpoint: List itemsIncorrect:
## List itemsCorrect:
## Endpoint: List items
Path: `GET /v1/items`Incorrect:
## Endpoint: List items
Path: `FETCH /v1/items`Correct:
### Logic
1. Return the items.Incorrect:
### ComponentsCorrect:
### Logic
### Components
### Mappings
### Examples
### ReferencesIncorrect:
### Mappings
### LogicCorrect:
#### Fetch data
##### DescriptionIncorrect:
#### Fetch data
###### DescriptionOnly with allow_unknown_sections = false.
Correct:
### ComponentsIncorrect:
### ExtrasCorrect:
### Logic
1. Return the items.Incorrect:
### Logic
### ComponentsCorrect:
#### Fetch data
##### Description
Read the items.Incorrect:
#### Fetch data
Read the items.Correct:
#### Fetch data
#### Save dataIncorrect:
#### Fetch data
#### Fetch dataCorrect:
#### Item mapping
| Source | Target |
| --- | --- |
| id | itemId |Incorrect:
#### Item mapping
| Source | Name |
| --- | --- |
| id | |Correct:
See [items][list].
[list]: items.mdIncorrect:
See [items][missing].Correct:
[list]: items.mdIncorrect:
[list]: no-such-file.mdCorrect:
[fetch]: items.md#fetch-dataIncorrect:
[fetch]: items.md#no-such-headingCorrect:
See [items][list].
[list]: items.mdIncorrect:
See [items](items.md).Correct:
### References
[list]: items.mdIncorrect:
[list]: items.md
### LogicReported as a warning, so a run without --strict still exits 0. --strict or diagnostics.warnings_as_errors = true turns it into an error.
Correct:
See [items][list].
[list]: items.mdIncorrect:
[list]: items.mdExamples are optional. Each one is a request or response H4 with a fenced code block, at most once per endpoint.
Correct:
### Examples
#### Request
```json
{"name": "backup"}
```
#### Response
```json
{"id": 1}
```Incorrect:
### Examples
#### Headers
Accept: application/jsonEvery general component must be linked from an endpoint or another component.
Correct:
## Endpoint: List items
Path: `GET /v1/items`
### Logic
1. [Check access][access].
## Components
### Check access
#### Description
Verify the caller token.
## References
[access]: #check-accessIncorrect:
## Components
### Check access
#### Description
Verify the caller token.An internal component may only be linked from its own endpoint. Move it to the general components section to share it.
Correct:
## Endpoint: List items
Path: `GET /v1/items`
### Logic
1. [Load items][load] first.
### Components
#### Load items
##### Description
Read the items.
### References
[load]: #load-itemsIncorrect:
## Endpoint: List items
Path: `GET /v1/items`
### Logic
1. Return the items.
### Components
#### Load items
##### Description
Read the items.
## Endpoint: Create item
Path: `POST /v1/items`
### Logic
1. [Load items][load] first.
### References
[load]: #load-itemsCorrect:
[labels]
logic = "Logic"Incorrect:
[labels]
logic = " Logic "Correct:
mdspec check docs/Incorrect:
mdspec check no-such-dir/Correct:
mdspec check docs/api.mdIncorrect:
mdspec check docs/notes.txtEvery task is also tagged with the release that ships it, e.g. v0.1.3, and that tag is added to tasks/tags. tatr ls -c :v0.1.3 lists what a release contains. New tasks get the next version tag, marked planned in tasks/tags until that release ships, then shipped.
Cargo.toml carries 0.0.0 on main: the released version is written by the release run, so the tag is the only place it lives.
Tasks live in tasks/ in the tatr layout: one tasks/<YYYYMMDD-HHMMSS>/TASK.md per task, with the time in UTC. Tags are described in tasks/tags. Reference the task ID in commits and PRs.
Install tools once:
cargo install just --version 1.58.0 --locked
cargo install cargo-llvm-cov --version 0.9.1 --locked
cargo install cargo-mutants --version 27.1.0 --locked
rustup component add llvm-tools-previewBefore opening a PR:
just fmt
git commit
just fulljust full runs everything CI runs, plus mutation testing. It needs committed files and publishes nothing.