mdv check
Validate Markdown documents against their contracts: in a terminal, in CI, or on stdin.
MOTHER, the Nostromo's computer, never sleeps. She doesn't argue, doesn't editorialize — she scans what's in front of her and reports exactly what's wrong.
mdv checkis your MOTHER for Markdown: point it at a file, get back a verdict and an exit code.
Usage
mdv check [files...] [options]
cat doc.md | mdv check - --schema schema.mdv.md
mdv check --pr body.mdArguments
[files.— Markdown documents or directories to check; default: every auto-discovered contract and the documents it governs. A *.mdv.md file or a GitHub template is checked as a contract.. . ] -— Read one document from stdin (--schema or --pr required).
Output
-f,--formatFORMAT— stylish (default), compact (one finding per line; what the VS Code$medivaproblemMatcher reads), json, sarif, github (GitHub Actions inline annotations; a clean run prints one ✓ line), pr-comment, or issue-comment. The comment formats render a Mediva CI comment; set its marker and grace period with the configcommentkey.--list— Print every checked document and its contract, not only failures.--fail-onLEVEL— error (default): a gate fails on an error; warning: on any finding; never: exit 0 even when a gate failed (JSON still names the gate that failed; a check that could not run still exits 2). Heuristic (advisory) findings never count as errors.
Scope
--new— Only check the documents this branch changed, or whose contract it changed: shorthand for --changed against the merge base with the default branch (GITHUB_BASE_REF, else origin/HEAD, else main). --new=REF picks the branch. Picks files; each is checked in full.--staged— Only check staged Markdown (git index), read as it will be committed. For pre-commit hooks. Picks files; each is checked in full.--changedREV-RANGE— Only check documents that changed ingit diff REV-RANGE, or whose contract did (e.g.origin/). Picks files; each is checked in full. Not the same as --change-set.main. . . HEAD
Contract selection
--schemaFILE— Use an explicit contract instead of discovering one.--pr— Use the repository's pull request template (.github/pull_request_template.md) as the contract —mdv check --pr body..md --titleTEXT— Supply the PR, issue, or document title validated by title rules.
Context
--contextFILE— Load JSON title, issue, and value context.--nowDATE— Inject the ISO date or datetime read by freshness rules; an explicit contextvalues.wins.now --change-setREV-RANGE— Data, not scope: feed the files and refs changed over REV-RANGE to contracts that check against@change/orpaths @change/. Does not pick files - that is --changed.refs --change-set-fileFILE— The same change-set data from a JSON file instead of git (mutually exclusive with --change-set).
Sources and resolution
--sourceNAME=FILE[,.— Pin a declared logical source NAME to a local FILE for this run; wins over the config loader and over a snapshot replay.. . ] --resolverURL— Override the config's external-reference resolver endpoint.--resolver-snapshotFILE— Replay an existing resolver snapshot, or resolve live and write one.--source-snapshotFILE— Replay an existing contract-source capture; if FILE is missing, note it and record instead.
Examples
mdv check # check every auto-discovered *.mdv.md → *.md pair
mdv check README.md # check one file against its README.mdv.md sibling
mdv check --schema schema.mdv.md doc.md # check a file against an explicit contract
mdv check schema.mdv.md # check a contract as its own worked example
cat doc.md | mdv check - --schema schema.mdv.md # check a document piped via stdin
mdv check --pr body.md # check a pull request description against .github/pull_request_template.md
gh pr view --json body -q .body | mdv check --pr - # check an open pull request's description
mdv check -f pr-comment body.md --schema pr.mdv.md # render findings as a Mediva CI pull-request comment
mdv check --new # only the documents your branch changed (merge base with the default branch)
mdv check --staged # in a pre-commit hook: the staged Markdown, as it will be committed
mdv check --changed main...HEAD -f github # in CI: annotate the documents a pull request changedstdin
echo "## Summary" | npx mediva check - --schema pr.mdv.md- needs --schema or --pr and must be the only target.
The zero-config scan
Run mdv check with no files and it walks the tree, validating every document a contract governs.
| Contract | Checks |
|---|---|
X. | the X. beside it |
_template. | every other *. in its folder |
. | the document at <path> |
GitHub template with mdv: rules | itself, compile-checked |
Most specific wins: mirror, sibling, then _template..
| Skipped by the walk | Detail |
|---|---|
node_modules, dist, hidden directories | always |
A directory whose . contains a * line | the whole subtree; no usable patterns (empty, comment-only) prunes nothing and says so |
A pattern . | gitignore-style patterns; the walk keeps walking |
npx mediva check .git/HOOKS.md --schema h.mdv.md # an explicit path reaches a pruned area
npx mediva check docs # a directory runs the same scan rooted thereWhen nothing gets checked
| Case | What you get |
|---|---|
| Every section of a contract misses the document | unmatched-contract |
| The scan pairs nothing | a note naming what . excluded |
nothingToCheck: "fail" in config | an empty run exits 2 |
| A file you name that no contract governs | the lint gate alone and a no-contract warning; exit 0 unless lint fails |
Output
README.md
State: what the document claims; clear it by doing the work or saying it is not done
3 error "Installation" is empty empty-section
help: Write the section's content, or "Not specified." if there is none
✖ 1 problem (1 error, 0 warnings)
mdv explain <rule-id> for detailsEvery rule id links to the diagnostics reference.
The gates run in order: lint (markdownlint's rules, set by config lint, plus the fence check), shape, then state. Only the first that fails is reported. Lint findings are always errors.
Colour
| Setting | Effect |
|---|---|
| terminal | coloured |
| pipe or file | plain, no escape bytes |
FORCE_COLOR=1 | coloured through a pipe (set it in CI) |
NO_COLOR=1 | plain; FORCE_COLOR wins if both are set |
--color / --no-color | override all |
Output formats
| Format | For |
|---|---|
stylish | people (default) |
compact | one line per finding (the VS Code $mediva matcher) |
json | tooling, stable codes |
sarif | SARIF 2.1.0 for code scanning |
github | inline annotations on the pull request |
pr-comment, issue-comment | a Mediva CI comment (config comment key) |
npx mediva check README.md --schema readme.mdv.md -f json | jq '.results[].diagnostics[].code'
npx mediva check README.md --schema readme.mdv.md -f sarif > mediva.sarif
npx mediva check body.md --schema pr.mdv.md -f pr-comment > comment.md-f json gives each file's gate (the first gate that failed, null when none did, or "could-not-run"), failed (whether the file fails the run), its diagnostics, and readout once shape passed. --fail-on never changes only the exit code: a file whose gate failed still says failed: true, and a check that could not run still exits 2.
github prints ::error, ::warning or ::notice (advisory) per finding:
::error file=docs/guide.md,line=7,col=1,title=mdv(placeholder-text)::Placeholder "TBD" left in "Notes"%0Ahelp: Write the real content, or "Not specified." if unknownOnly what a change touched
npx mediva check --changed origin/main...HEAD -f github # CI
npx mdv check --new # locally, includes uncommitted work| Flag | Does |
|---|---|
--changed REV-RANGE | only documents in git diff REV-RANGE, or whose contract changed |
--new | --changed against the default branch's merge base |
--staged | only staged documents, read from the git index |
--change-set | not scope: feeds @change/ and @change/ rules |
These pick which documents run, and nothing else: each picked document is checked in full, its old problems with its new ones.
Pre-commit hooks
npx mdv check --stagedReads the git index. See the pre-commit recipe.
Blocking and advisory findings
--fail-on | Exits 1 on |
|---|---|
error (default) | a blocking error |
warning | any warning; a gate fails on its warnings, so a shape warning stops state |
never | nothing |
Heuristic rules (vague-evidence, @agent/, truncated-output, …) are advisory: they never fail under --fail-on error.
Checking a contract
npx mediva check pr.mdv.mdNaming a *. checks the contract itself: it compiles, its body passes as its own example, and every exemplar value occurs in it.
Exit codes
| Code | Meaning |
|---|---|
0 | no errors |
1 | errors (any finding with --fail-on warning) |
2 | could not run: bad option, unreadable file, broken config, broken contract |
CI recipes: PR bodies, issue forms, monorepo, changelogs.
External context
{ "issues": [{ "number": 123, "state": "open" }], "title": "Ship the docking checklist" }npx mediva check PR.md --schema pr.mdv.md --context context.jsonissue. reads it, else missing-context. --title ". sets context.; there is no H1 fallback.
Configuration
What blocks, severity overrides, plugins: Configuration.