| 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.
Conventions answer "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:. Setting an unknown name is a config
error at load time.
Built-in values: portable, github, plain. 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 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.
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 land on top of the
convention's Enabled: true and silently disable
the rule.
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: falseThe convention: selector lives at the top level.
So the user can disable MDS034 cleanly with a
bool-only markdown-flavor: false entry in the
rules block. The convention preset has already
populated the merged config at load time. A
bool-only later layer 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.
This split keeps MDS034 focused on "what does this renderer interpret as a feature." Conventions orchestrate style separately.
The three 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.
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, and plain
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, our-team, plain, portable)
User-defined conventions apply as a base layer, exactly
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. Built-in conventions
carry no suffix. Example merge-chain output:
convention.our-team (user) set {flavor: gfm}
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.