| weight | 20 |
|---|---|
| summary | Built-in Markdown conventions, the rule presets each one applies, and how user config layers on top via deep-merge. |
A convention is an opinionated bundle of rule
settings that pairs a Markdown flavor with a set of
style choices. Setting convention: at the top of
your .mdsmith.yml selects one of the built-in
bundles; the rule presets in that bundle are applied
as a base layer beneath your own rule config. It
answers "what kind of Markdown does this project
write?" with one config knob instead of eight.
A convention is distinct from a flavor. Flavor is a property of the renderer (CommonMark, GFM, goldmark — what the parser interprets). Convention is a property of the project (the team's writing choices among forms the renderer treats equally). See the concepts doc for the full picture and where the concepts overlap.
convention: portableThat single line pins a flavor and a curated set of
style-rule settings. convention: is a top-level
config key, sibling to rules:, kinds:, and
overrides:. An unknown name is a config error.
Built-in values: portable, github, obsidian,
parity, plain, no-llm-tells. The key is
optional; omit it for no convention.
You may also set flavor: inside markdown-flavor
alongside convention:. If both are set, they must
agree — a convention that requires commonmark
rejects flavor: gfm at config load.
Markdown that renders the same in every CommonMark
parser. Selects flavor: commonmark and turns on
the strict-style rules with their recommended
defaults.
| Rule | Setting |
|---|---|
markdown-flavor |
flavor: commonmark |
no-inline-html |
enabled |
no-reference-style |
allow-footnotes: false |
emphasis-style |
bold: asterisk, italic: underscore |
horizontal-rule-style |
style: dash, length: 3, require-blank-lines: true |
list-marker-style |
style: dash |
ordered-list-numbering |
style: sequential, start: 1 |
ambiguous-emphasis |
max-run: 2 |
Markdown that renders well on github.com. Selects
flavor: gfm and keeps the style rules light: the
inline-HTML allowlist permits <details> and
<summary>; emphasis and list-marker style are
pinned for consistency; the rest of the strict
rules stay off.
| Rule | Setting |
|---|---|
markdown-flavor |
flavor: gfm |
no-inline-html |
allow: [details, summary] |
emphasis-style |
bold: asterisk, italic: underscore |
list-marker-style |
style: dash |
Markdown written in an Obsidian vault. Selects
flavor: gfm and turns on the Obsidian-specific
validations — MDS027 resolves [[Page]] wikilink
targets workspace-wide, and MDS067 checks every
[!type] callout against the Obsidian type set.
| Rule | Setting |
|---|---|
markdown-flavor |
flavor: gfm |
cross-file-reference-integrity |
wikilinks: true, wikilink-style: obsidian |
callout-type |
enabled (12 base Obsidian types and aliases) |
Standard style rules stay at their defaults so an Obsidian vault behaves like a GFM project unless the team layers more rules on top.
To run these checks inside the editor, install the Obsidian plugin. It hosts the same engine as a WebAssembly runtime.
Markdown that survives cat. The rendered output
should look about the same as the source viewed in
a plaintext reader. Same activations as portable,
plus allow-comments: false on no-inline-html so
HTML comments do not leak through as literal
<!-- ... --> text.
A truly plaintext-faithful convention needs three
more rules. One forbids * and _ runs. One
requires indented code blocks. One inverts
no-bare-urls so bare URLs are preferred over
Markdown links. Those rules don't exist yet. When
they ship, the plain convention gains them and
diverges from portable.
Markdown checked the way the
markdownlint-compatible tools (mado, rumdl) check
it: flavor: gfm plus every mdsmith-only rule
turned off, leaving the structural style class
those tools also run. Use it for a like-for-like
comparison, or as a fast markdownlint gate before
you adopt the cross-file and generated-section
layer. The disabled-rule list is generated from the
convention, so it never drifts — see the
benchmark doc. Unlike the other
built-ins, parity leaves markdown-flavor
(MDS034) opt-in.
Flags mechanical LLM-prose tells in CI. MDS056
blocks vocabulary and phrasal tells; MDS055 blocks
banned sentence openers; MDS023 and MDS024 tighten
readability budgets. Lists are sourced from
slop-patterns.md; a drift-checker test
keeps the two in sync.
Pins no flavor and does not enable markdown-flavor
(MDS034). The contains and starts lists merge by
append: a project's own entries join the
convention's list instead of replacing it.
Convention presets sit between built-in defaults and your explicit top-level rules. The merge order, oldest → newest, is:
default— built-in defaults: rules incfg.Rulesthat you did not setconvention.<name>— the preset tableuser— your top-level rules block (rules you explicitly set in.mdsmith.yml)kinds.<name>— each kind in the file's effective listoverrides[i]— each matching override entry
Each layer deep-merges onto the previous one.
Scalars at a leaf are replaced by the later layer;
maps recurse key by key; lists replace by default.
A convention preset provides the floor; your
explicit rules: block overrides on top.
The default and user layers come from the same
cfg.Rules map. mdsmith splits them around the
convention so a convention can enable a rule that
is opt-in by default (e.g. convention: portable
turns on MDS034). Without the split, the default's
Enabled: false would override the convention's
Enabled: true.
For example, the github convention sets
no-inline-html.allow: [details, summary]. To
extend the allowlist with <sub> and <sup>,
write:
convention: github
rules:
no-inline-html:
allow: [sub, sup]Lists default to replace, so the effective
allowlist becomes [sub, sup]. To keep the
preset's entries, list them explicitly:
allow: [details, summary, sub, sup].
A convention applies its rule presets at config
load time. Disabling markdown-flavor itself does
not disable the rules a convention turned on.
convention: portable
rules:
markdown-flavor: falseA bool-only markdown-flavor: false entry toggles
enabled without erasing the preset's settings. The
rule stays configured but its Check() is gated
off. The other rules in the preset are untouched.
The built-in conventions cover common cases. Teams
that need something custom define it inline in
.mdsmith.yml. The top-level conventions: key holds
the map:
conventions:
our-team:
flavor: gfm
rules:
no-inline-html:
allow: [details, summary, kbd]
list-marker-style:
style: dash
no-reference-style:
allow-footnotes: true
convention: our-teamEach entry is a { flavor, rules } pair. The rules
block uses the same schema as the top-level rules:
block. To lift one out of .mdsmith.yml into its own
file under .mdsmith/conventions/<name>.yaml, see the
convention files reference.
User-defined conventions are validated at config load:
flavormust be a recognised flavor string such ascommonmark,gfm, orgoldmark.- Each key under
rules:must name a registered rule. - Each rule's settings must pass the rule's own schema check.
Validation errors name the convention and the rule:
convention "our-team" rule "no-inline-html": no-inline-html: unknown setting "allowed"
The built-in names portable, github,
obsidian, parity, plain, and no-llm-tells
are reserved. Defining a conventions.portable
entry is a config error. This keeps the built-in
names stable across docs and tutorials.
The lookup checks user-defined conventions first, then falls back to the built-in table. Collisions with reserved names are rejected at load time, so shadowing is impossible. When neither table matches, the error lists both sets:
unknown convention "bogus" (valid: github, no-llm-tells, obsidian, our-team, parity, plain, portable)
User-defined conventions apply as a base layer, like
the built-in conventions. A top-level rules: entry
overrides the convention preset for that rule; the
rest of the preset remains. mdsmith kinds resolve <file> labels user-convention layers with a
(user) suffix.
mdsmith kinds resolve <file> shows the merge
chain for every rule, including the
convention.<name> layer. Use it to confirm which
value won and where it came from.