The `house-vale-3-15-2-repro` provenance entry claimed behaviours were reproduced against purpose-built fixtures, but no fixtures existed, so the earlier commit in this PR removed it. Commit the fixtures. tests/test-vale-3-15-2-behaviours.sh builds its fixtures in a temp dir and runs the real Vale. It exits 77 (skipped) when vale is missing or is not 3.15.2. It asserts the six vale-config behaviours and the vale-run ones (unmapped .mdx, `vale off` variants, the spelling ignore file, and the ls-* commands never naming a rule). Restore the entry in both sources.md files as `Research doc: none` with `Basis:` naming the test, and re-add its source_keys. Two behaviours are not asserted: the native-MDX suppression column (needs mdx2vast) and the `vale sync` row that adds to Packages (needs the network). The wording in configuration-reference.md and troubleshooting.md now says so. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EGHFJextYtVQseaHPDDhxB
6.8 KiB
topic, source_keys
| topic | source_keys | ||
|---|---|---|---|
| configuration-reference |
|
Core Settings
| Key | Type | Purpose |
|---|---|---|
StylesPath |
string | Path to all Vale-related resources (styles, dictionaries, vocab). |
Packages |
string[] | Packages to download and install via vale sync. |
Vocab |
string[] | Vocabularies to load. |
MinAlertLevel |
enum | Minimum severity to report: suggestion, warning, or error. |
IgnoredScopes |
enum | Inline-level HTML tags to ignore. |
SkippedScopes |
enum | Block-level HTML tags to ignore entirely. |
Format Associations
Map an unrecognized extension onto a supported one so Vale lints it with the right parser — an extension-level substitution only, it does not add new file-type support:
[formats]
mdx = md
mdx is the case that matters, because Vale 3.15.2 has no built-in MDX support. The mapping above is not cosmetic: it is what lets .mdx files lint with nothing else installed. Leave it out and Vale takes the native MDX path, which shells out to an external mdx2vast binary — absent from PATH, the run dies with E100 [lintMDX] Runtime error / mdx2vast not found, exit 2, and every other file in the same invocation goes unlinted too. Take the mapping: it is this skill's recommended default, because it needs nothing installed, and it is the branch vale-run assumes when it documents inline suppressions. Install mdx2vast (npm install -g mdx2vast) only when something else in the toolchain already needs the native MDX parser.
The choice also decides the inline-suppression syntax, and it is inverted between the two: mapped to md, .mdx takes Markdown's <!-- vale off -->; native, it takes {/* vale off */}. vale-run's references/troubleshooting.md carries the verified matrix.
Vocabularies
Reference a named vocabulary (a folder of accept/reject word lists under StylesPath) via Vocab, then apply styles per glob:
StylesPath = styles
Vocab = Blog
[*]
BasedOnStyles = Vale, MyStyle
Local Overrides
A project can layer a local .vale.ini that overrides StylesPath, adds packages, and changes BasedOnStyles for a subset of files — local settings merge with or override the global ones:
StylesPath = localpath
Packages = write-good
[*.md]
BasedOnStyles = write-good
Rule Header Fields
Individual rule YAML files (under a style's directory) support these header fields:
| Field | Required | Default | Purpose |
|---|---|---|---|
extends |
yes | — | Check this rule extends (e.g. existence). |
message |
yes | — | Message shown when triggered; supports %s formatting per check type. |
level |
no | suggestion |
Severity: suggestion, warning, or error. |
scope |
no | text |
Scope the rule applies to (e.g. heading). |
link |
no | — | URL with more info about the rule. |
limit |
no | — | Max number of triggers per file. |
vocab |
no | true |
Set false to disable active vocabularies for this rule. |
Checks
The underlying functions a rule's extends field can reference: existence, substitution, occurrence, repetition, consistency, conditional, capitalization, metric, spelling, sequence, script.
Style Resolution
Only package styles need fetching. A style whose YAML rule files are already committed under StylesPath lints immediately, with no Packages entry and no vale sync; the same is true of the built-in Vale style, which ships with the binary and contains four rules:
Vale.Spelling— spell-checks against Hunspell-compatible dictionaries in<StylesPath>/config/dictionaries.Vale.Terms— enforces the project's accepted vocabulary terms.Vale.Avoid— enforces the project's rejected vocabulary terms.Vale.Repetition— flags repeated words (e.g. "the the").
Packages (top-level, what vale sync downloads) and BasedOnStyles (per-glob, what activates) are separate keys: a style lints a file only once it is in both. Every row below is asserted against Vale 3.15.2 by tests/test-vale-3-15-2-behaviours.sh (slug house-vale-3-15-2-repro) except the vale sync row that adds the name to Packages, which needs the network and is not covered:
| Configuration | Result |
|---|---|
BasedOnStyles names a style with no directory under StylesPath, not built-in |
E100 [loadStyles] Runtime error — style 'X' does not exist on StylesPath, exit 2 |
StylesPath directory itself absent, even with only Vale active |
E201 Invalid value — The path '...' does not exist, exit 2 |
vale sync with a name in BasedOnStyles but not Packages |
SUCCESS Synced 0 package(s), exit 0, nothing downloaded — the next lint repeats the E100 |
vale sync with the name added to Packages |
package lands under StylesPath, exit 0; lint then loads it |
Style in Packages and synced, but in no glob's BasedOnStyles |
0 findings, exit 0 — downloads, never lints, indistinguishable from a clean run |
BasedOnStyles names an empty directory under StylesPath |
0 findings, exit 0 — loads and lints nothing; vale sync never produces this state |
Built-in Vale, or a style's YAML committed under StylesPath |
lints immediately, no Packages entry, no sync |
Core option (StylesPath, MinAlertLevel, Vocab, IgnoredScopes, SkippedScopes) below a [glob] header |
E201 Invalid value — 'X' is a core option; it should be defined above any syntax-specific options ([...]), exit 2 |
Packages below a [glob] header |
no error, exit unaffected — parsed as a per-glob rule toggle (SChecks: {"*.md": {"Packages": false}} in ls-config), so vale sync reports Synced 0 package(s) and downloads nothing |
Frontmatter Scopes
House-verified behaviour, not documented on vale.sh — asserted against Vale 3.15.2 by tests/test-vale-3-15-2-behaviours.sh (slug house-vale-3-15-2-repro).
A rule scoped to text.frontmatter.<key> (e.g. text.frontmatter.description) matches reliably when that field's value is a single physical line, and breaks on most — not all — multi-line forms. Multi-line forms spanning 2+ lines:
| Frontmatter value form | Result |
|---|---|
| Single physical line (control) | Lints, exits 1 |
| literal block scalar |
Lints, exits 1 |
> folded block scalar |
0 findings, exits 0 |
| Plain (unquoted) continuation lines | 0 findings, exits 0 |
| Single- or double-quoted multi-line scalar | 0 findings, exits 0 |
The silent cases produce no error of any kind, so a passing run is indistinguishable from a clean one. Do not assume a literal block scalar and a folded one behave alike — reproduce both against your own config before trusting a frontmatter-scoped rule in production. If the field is commonly authored in one of the broken forms, flatten it to one physical line ahead of the vale call rather than relying on the scope alone.