refactor(skills): retrofit the corpus to the ADR-0020 context contract #129

Merged
Defame1297 merged 89 commits from refactor/adr0020-skill-retrofit into main 2026-09-01 13:47:47 +00:00
12 changed files with 224 additions and 28 deletions
Showing only changes of commit b07d54ad7a - Show all commits

View File

@@ -8,7 +8,7 @@ description: >
metadata: metadata:
category: lint category: lint
version: "0.1.1" version: "0.1.2"
source_keys: source_keys:
- context7-websites-vale-sh - context7-websites-vale-sh
- house-vale-3-15-2-repro - house-vale-3-15-2-repro
@@ -19,7 +19,7 @@ metadata:
- A style in `BasedOnStyles` that is neither built-in nor a directory under `StylesPath` fails hard, not silently: `E100 [loadStyles]`, exit 2, nothing linted. - A style in `BasedOnStyles` that is neither built-in nor a directory under `StylesPath` fails hard, not silently: `E100 [loadStyles]`, exit 2, nothing linted.
- `vale sync` alone does not clear that `E100`. Sync fetches only what the top-level `Packages` key declares, so against a `BasedOnStyles`-only name it reports `Synced 0 package(s)` and exits 0, fetching nothing. Add the style to `Packages`, then sync. A style lints only once it is in both keys — and the reverse case is silent, exiting 0. - `vale sync` alone does not clear that `E100`. Sync fetches only what the top-level `Packages` key declares, so against a `BasedOnStyles`-only name it reports `Synced 0 package(s)` and exits 0, fetching nothing. Add the style to `Packages`, then sync. A style lints only once it is in both keys — and the reverse case is silent, exiting 0.
- Only *package* styles need fetching: built-in `Vale`, and any style whose YAML is already committed under `StylesPath`, lint with no `Packages` entry and no sync. - Only *package* styles need fetching: built-in `Vale`, and any style whose YAML is already committed under `StylesPath`, lint with no `Packages` entry and no sync.
- `.vale.ini` is order-sensitive: core settings first, then `[formats]`, then glob sections. Anything written below a glob header applies only to files matching that glob. - `.vale.ini` order is enforced, not stylistic: put core settings first, then `[formats]`, then glob sections. A core setting (`StylesPath`, `MinAlertLevel`, `Vocab`, `IgnoredScopes`, `SkippedScopes`) written below a `[glob]` header is a hard error — `E201 ... 'StylesPath' is a core option; it should be defined above any syntax-specific options`, exit 2, nothing linted. `Packages` is the exception, and the worse one: below a glob header it is accepted with no error, then ignored — `vale sync` reports `Synced 0 package(s)` and downloads nothing.
- A rule scoped to `text.frontmatter.<key>` silently matches nothing when the field spans multiple lines in most YAML forms. If you scope a rule to frontmatter, read `references/configuration-reference.md` first. - A rule scoped to `text.frontmatter.<key>` silently matches nothing when the field spans multiple lines in most YAML forms. If you scope a rule to frontmatter, read `references/configuration-reference.md` first.
## Setup workflow ## Setup workflow
@@ -34,7 +34,7 @@ metadata:
[*.md] [*.md]
BasedOnStyles = Vale BasedOnStyles = Vale
``` ```
`Vale` here is the built-in style (`Vale.Spelling`, `Vale.Terms`, `Vale.Avoid`, `Vale.Repetition`) — no download needed, it always works. `Vale` here is the built-in style (`Vale.Spelling`, `Vale.Terms`, `Vale.Avoid`, `Vale.Repetition`): no `Packages` entry and no `vale sync`. It still needs the `StylesPath` directory to exist — declare `StylesPath = styles` without creating `styles/` and even a `Vale`-only config dies with `E201 ... The path '...' does not exist`, exit 2. That is why the previous step creates the directory.
- [ ] **Add third-party styles** (optional) by declaring them in `Packages`, then activating them in the same or another glob's `BasedOnStyles`: - [ ] **Add third-party styles** (optional) by declaring them in `Packages`, then activating them in the same or another glob's `BasedOnStyles`:
```ini ```ini
Packages = Google, write-good Packages = Google, write-good

View File

@@ -25,6 +25,10 @@ Map an unrecognized extension onto a supported one so Vale lints it with the rig
mdx = md 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. Install it with `npm install -g mdx2vast` or take the mapping.
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 ## Vocabularies
Reference a named vocabulary (a folder of accept/reject word lists under `StylesPath`) via `Vocab`, then apply styles per glob: Reference a named vocabulary (a folder of accept/reject word lists under `StylesPath`) via `Vocab`, then apply styles per glob:
@@ -89,6 +93,8 @@ Only *package* styles need fetching. A style whose YAML rule files are already c
| Style in `Packages` and synced, but in no glob's `BasedOnStyles` | 0 findings, exit 0 — downloads, never lints, indistinguishable from a clean run | | 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 | | `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 | | 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 ## Frontmatter Scopes

View File

@@ -11,7 +11,7 @@
## house-vale-3-15-2-repro ## house-vale-3-15-2-repro
- **URL:** (house-verified — reproduced locally against the `vale` binary, not an external source) - **URL:** (house-verified — reproduced locally against the `vale` binary, not an external source)
- **Description:** Behaviour of Vale 3.15.2 established by running it against purpose-built fixtures in this repo, where vale.sh documents nothing: the `E100 [loadStyles]` / exit-2 failure for a `BasedOnStyles` name absent from `StylesPath`, `vale sync` reporting `Synced 0 package(s)` for a name not declared in `Packages`, the `E201` / exit-2 failure when the `StylesPath` directory does not exist, the exit-0 no-op of an empty style directory, and the `text.frontmatter.<key>` scope matrix across multi-line YAML forms. - **Description:** Behaviour of Vale 3.15.2 established by running it against purpose-built fixtures in this repo, where vale.sh documents nothing: the `E100 [loadStyles]` / exit-2 failure for a `BasedOnStyles` name absent from `StylesPath`, `vale sync` reporting `Synced 0 package(s)` for a name not declared in `Packages`, the `E201` / exit-2 failure when the `StylesPath` directory does not exist, the exit-0 no-op of an empty style directory, the `E201` / exit-2 failure when a core option is written below a `[glob]` header (with `Packages` as the silent exception), and the `text.frontmatter.<key>` scope matrix across multi-line YAML forms.
- **Research doc:** none — house-verified reproduction, not part of the plugin's research corpus (no `plugins/lint/docs/research/` topic file backs this entry) - **Research doc:** none — house-verified reproduction, not part of the plugin's research corpus (no `plugins/lint/docs/research/` topic file backs this entry)
- **Contributing files:** SKILL.md, references/configuration-reference.md - **Contributing files:** SKILL.md, references/configuration-reference.md
- **Status:** `extracted` - **Status:** `extracted`

View File

@@ -6,16 +6,17 @@ description: >
as in "lint the docs", "check prose style", or "why is CI failing on the docs as in "lint the docs", "check prose style", or "why is CI failing on the docs
check". Not setting up Vale config or styles -> `vale-config`. check". Not setting up Vale config or styles -> `vale-config`.
metadata: metadata:
version: "0.1.2" version: "0.1.3"
category: lint category: lint
source_keys: source_keys:
- context7-websites-vale-sh - context7-websites-vale-sh
- house-vale-3-15-2-repro
--- ---
## Gotchas ## Gotchas
- Vale's exit code keys off `error`-level alerts only — `warning` and `suggestion` alerts print and still exit `0`, and `MinAlertLevel`/`--minAlertLevel` filter what is displayed, never the exit code — no flag makes warnings fail. A rule that must gate CI or a commit hook has to be `level: error`. This is the most common way a Vale gate silently passes everything. - Vale's exit code keys off `error`-level alerts only — `warning` and `suggestion` alerts print and still exit `0`, and `MinAlertLevel`/`--minAlertLevel` filter what is displayed, never the exit code — no flag makes warnings fail. A rule that must gate CI or a commit hook has to be `level: error`. This is the most common way a Vale gate silently passes everything.
- Check whether the target repo documents its own `vale` wrapper script (README, CONTRIBUTING, pre-commit config, `scripts/`) before calling the binary. Some projects wrap `vale` to work around real bugs — e.g. a scope that silently stops matching multi-line YAML block-scalar frontmatter — so bare `vale` skips whatever the wrapper fixes. Invoke the documented wrapper with the same arguments. - Check whether the target repo documents its own `vale` wrapper script (README, CONTRIBUTING, pre-commit config, `scripts/`) before calling the binary. Some projects wrap `vale` to work around real bugs — e.g. a `text.frontmatter.<key>` scope that silently stops matching multi-line values (`>` folded scalars, plain continuation lines and quoted multi-line scalars all go unmatched; a `|` literal block scalar still works) — so bare `vale` skips whatever the wrapper fixes. Invoke the documented wrapper with the same arguments.
## Running vale ## Running vale
@@ -34,20 +35,22 @@ Key flags:
| `--no-exit` | Suppresses the nonzero exit that `error`-level alerts would otherwise cause; a no-op when no rule is `error`-level. Use in CI stages that should surface lint output without hard-failing the build. | | `--no-exit` | Suppresses the nonzero exit that `error`-level alerts would otherwise cause; a no-op when no rule is `error`-level. Use in CI stages that should surface lint output without hard-failing the build. |
| `--ignore-syntax` | Treats input as plain text, skipping format-aware parsing — use when a file's syntax-aware parser produces noisy or wrong results. | | `--ignore-syntax` | Treats input as plain text, skipping format-aware parsing — use when a file's syntax-aware parser produces noisy or wrong results. |
`vale sync` downloads the packages/styles declared in `.vale.ini` — that's a one-time-per-change setup step (vale-config's territory), not part of a normal lint run. If a run behaves as though no styles are active, check `vale ls-config` to confirm what actually loaded before concluding it is a sync problem — an unrun `vale sync` is the usual cause, and not a `vale-run` problem. `vale sync` downloads the packages/styles declared in `.vale.ini` — that's a one-time-per-change setup step (vale-config's territory), not part of a normal lint run. If a run behaves as though no styles are active, check `vale ls-config` before concluding it is a sync problem — an unrun `vale sync` is the usual cause, and not a `vale-run` problem. `ls-config` resolves config files, styles and `StylesPath` search paths; it never enumerates rules, so it cannot tell you a given rule is live.
Prefer `--output=JSON` whenever the caller (a script, a CI step, another agent) needs to act on individual alerts rather than just get a pass/fail signal — `CLI` and `line` are for humans reading the terminal. Prefer `--output=JSON` whenever the caller (a script, a CI step, another agent) needs to act on individual alerts rather than just get a pass/fail signal — `CLI` and `line` are for humans reading the terminal.
## Fixing false positives ## Fixing false positives
Before writing any inline suppression markup (steps 2 and 3 below), read `references/troubleshooting.md` for your file's format: the markup is format-specific, and the wrong form suppresses nothing while Vale reports no error — the Markdown HTML-comment form is inert in an MDX file. Before writing any inline suppression markup (steps 2 and 3 below), read `references/troubleshooting.md`: the form follows the parser the config picks, not the file extension, and the wrong form suppresses nothing while Vale reports no error.
`.mdx` is the trap — the two parsers take opposite forms, so read `.vale.ini` first. Under `[formats] mdx = md` (what `vale-config` recommends) it is Markdown: `<!-- vale off -->` suppresses, `{/* vale off */}` does not. Without that mapping Vale takes the native MDX path, which needs the external `mdx2vast` binary (`npm install -g mdx2vast`); missing, the whole invocation dies — `E100 [lintMDX] ... mdx2vast not found`, exit 2 — leaving every other file in the run unlinted too.
Scope the fix as narrowly as possible, in this order: Scope the fix as narrowly as possible, in this order:
1. **Mentioning banned phrasing rather than using it**: wrap it in backticks or a fenced code block. Vale skips code spans and fences, so no suppression is needed at all. Try this before any suppression markup. 1. **Mentioning banned phrasing rather than using it**: wrap it in backticks or a fenced code block. Vale skips code spans and fences, so no suppression is needed at all. Try this before any suppression markup.
2. **One-off**: inline-suppress the specific text run with the format's `vale off`/`vale on` markup. 2. **One-off**: inline-suppress the specific text run with the format's `vale off`/`vale on` markup.
3. **Recurring known-exception string, one rule**: disable that specific rule for that specific match inline (in Markdown, e.g. `<!-- vale Style.Redundancy["ACT test","OTHER"] = NO -->` ... `= YES`), rather than the whole rule. 3. **Recurring known-exception string, one rule**: disable that specific rule for that specific match inline (in Markdown, e.g. `<!-- vale Style.Redundancy["ACT test","OTHER"] = NO -->` ... `= YES`), rather than the whole rule.
4. **Known project term failing spell check**: add it to the style's `ignore` list, not an inline suppression. 4. **Known project term failing spell check**: add it to the style's `ignore` list, not an inline suppression — and put the listed file at the `StylesPath` root, because an `ignore` path that resolves nowhere is a silent no-op (`references/troubleshooting.md`).
Never disable a rule project-wide to fix one false positive — editing `.vale.ini`/`BasedOnStyles` is vale-config's job, and it silences the rule everywhere, not just the false-positive case. Never disable a rule project-wide to fix one false positive — editing `.vale.ini`/`BasedOnStyles` is vale-config's job, and it silences the rule everywhere, not just the false-positive case.

View File

@@ -7,3 +7,11 @@
- **Research doc:** plugins/lint/docs/research/docs/vale/sources.md - **Research doc:** plugins/lint/docs/research/docs/vale/sources.md
- **Contributing files:** SKILL.md, references/troubleshooting.md - **Contributing files:** SKILL.md, references/troubleshooting.md
- **Status:** `extracted` - **Status:** `extracted`
## house-vale-3-15-2-repro
- **URL:** (house-verified — reproduced locally against the `vale` binary, not an external source)
- **Description:** Behaviour of Vale 3.15.2 established by running it against purpose-built fixtures in this repo, where vale.sh documents nothing or documents it wrongly: `.mdx` has no built-in support and needs either `[formats] mdx = md` or an external `mdx2vast` binary (absent, the whole invocation exits 2 with `E100 [lintMDX]`), the inline-suppression form inverts between those two configurations, the `spelling` check's `ignore` paths resolve against `StylesPath` or the working directory but never against the rule file's own directory and fail silently when they resolve nowhere, `ls-config` reports styles and paths but never rules, and the `text.frontmatter.<key>` scope matrix across multi-line YAML forms.
- **Research doc:** none — house-verified reproduction, not part of the plugin's research corpus (no `plugins/lint/docs/research/` topic file backs this entry)
- **Contributing files:** SKILL.md, references/troubleshooting.md
- **Status:** `extracted`

View File

@@ -1,6 +1,7 @@
--- ---
source_keys: source_keys:
- context7-websites-vale-sh - context7-websites-vale-sh
- house-vale-3-15-2-repro
--- ---
# Vale troubleshooting reference # Vale troubleshooting reference
@@ -9,19 +10,68 @@ source_keys:
`vale ls-config` prints the fully-resolved, currently active configuration as JSON. Check that `vale ls-config` prints the fully-resolved, currently active configuration as JSON. Check that
before rereading `.vale.ini` — what is written in the config file is not necessarily what is before rereading `.vale.ini` — what is written in the config file is not necessarily what is
active, and the resolved output is the fastest way to see which styles and rules a run actually active, and the resolved output is the fastest way to see which config files, styles and
loaded. `StylesPath` directories a run actually loaded.
It stops at styles. It does not enumerate rules, and neither does any other Vale 3.15.2
subcommand — `ls-config`, `ls-dirs`, `ls-vars` and `ls-metrics` were each checked and none
names the rule. Against a config
whose custom rule was demonstrably firing on the target file, `ls-config` reported
`"SBaseStyles": {"*.md": ["MyStyle"]}` and the two `StylesPath` search paths, but
`"Checks": null`, `"SChecks": {"*.md": {}}` and `"RuleToLevel": {}` — the firing rule's own name
appeared nowhere in the output. So `ls-config` answers "is this style loaded, and from where",
not "is this rule live". For the latter, run Vale over a small fixture that should trigger the
rule and see whether it alerts.
## Inline suppression syntax by format ## Inline suppression syntax by format
Markdown uses HTML comments — the MDX `{/* */}` form does not suppress anything in a plain `.md` file: ### Prerequisite: `.mdx` needs a decision before it lints at all
Vale 3.15.2 has no built-in MDX support. If `.vale.ini` does **not** map the extension, Vale takes
the native MDX path and shells out to an external `mdx2vast` binary. Without it on `PATH` the run
dies before linting anything:
```
$ vale . # doc.md and doc.mdx both present, no [formats] mapping
E100 [lintMDX] Runtime error
mdx2vast not found
Execution stopped with code 1.
$ echo $?
2
```
That is the whole invocation, not just the `.mdx` file — the `.md` alongside it produced no output
either. Fix it one of two ways, and the choice is not cosmetic because it also decides the
suppression syntax:
| `.vale.ini` | Prerequisite | Parser | Suppression form that works |
|---|---|---|---|
| `[formats]` maps `mdx = md` (what `vale-config` recommends) | none | Markdown | `<!-- vale off -->` |
| no `mdx` mapping (native MDX) | `npm install -g mdx2vast` | MDX | `{/* vale off */}` |
Key the markup to that config row, never to the file extension. Verified against Vale 3.15.2, same
three fixtures under each config:
| File | Mapped `mdx = md` | Native MDX (`mdx2vast` installed) |
|---|---|---|
| no suppression (control) | alert, exit 1 | alert, exit 1 |
| `<!-- vale off -->` | suppressed, exit 0 | `E100 ... failed to parse MDX: Unexpected character` `` `!` ``, exit 2 |
| `{/* vale off */}` | **not suppressed**, exit 1 | suppressed, exit 0 |
Under the mapping the JSX comment is worse than inert: it is linted as prose, so
`{/* vale Vale.Repetition = NO */}` produced three alerts where the un-suppressed file produced
one — its own markup tripped the rule twice more.
Markdown, and `.mdx` mapped onto it — HTML comments:
```markdown ```markdown
<!-- vale off --> <!-- vale off -->
This text will be ignored. This text will be ignored.
<!-- vale on --> <!-- vale on -->
``` ```
MDX: Native MDX only (no `[formats]` mapping, `mdx2vast` on `PATH`):
```mdx ```mdx
{/* vale off */} {/* vale off */}
This text will be ignored. This text will be ignored.
@@ -46,7 +96,8 @@ This is some text ACT test
<!-- vale Style.Redundancy["ACT test","OTHER"] = YES --> <!-- vale Style.Redundancy["ACT test","OTHER"] = YES -->
``` ```
MDX: Native MDX only — under `[formats] mdx = md` an `.mdx` file takes the Markdown form above, and
this one silences nothing:
```mdx ```mdx
{/* vale Style.Redundancy["ACT test","OTHER"] = NO */} {/* vale Style.Redundancy["ACT test","OTHER"] = NO */}
This is some text ACT test This is some text ACT test
@@ -66,6 +117,36 @@ ignore:
- ignore2.txt - ignore2.txt
``` ```
**Where the file goes, and why a wrong answer is invisible.** Each entry resolves against the
`StylesPath` root, or against the working directory `vale` is invoked from. It does **not** resolve
against the rule file's own directory — which is the natural reading of the YAML above, since the
path sits inside the rule, and it is wrong. Verified against Vale 3.15.2 across four fresh trees,
each with the same rule and the same unknown word:
| Where `ignore1.txt` was placed | Result |
|---|---|
| `<StylesPath>/ignore1.txt` | word ignored, exit 0 |
| `./ignore1.txt` in the directory `vale` runs from | word ignored, exit 0 |
| `<StylesPath>/<Style>/ignore1.txt`, beside the rule | word still flagged, exit 1 |
| file absent entirely | word still flagged, exit 1 |
The two failing rows emit no warning, no error, and no diagnostic of any kind: the output is
byte-identical to the same rule with the `ignore` key deleted. A misplaced ignore list looks exactly
like a list that was read and did not contain the word.
Prefer the `StylesPath` root. The run-directory form is the fragile one — move the invocation and it
silently stops working. Same tree, same file, only the working directory changed:
```
$ cd project && vale doc.md # ignore1.txt at project root
✔ 0 errors, 0 warnings and 0 suggestions in 1 file. # exit 0
$ cd /elsewhere && vale --config=project/.vale.ini project/doc.md
1:5 error Did you really mean 'zzqwidget'? MyStyle.Spell # exit 1
```
The `StylesPath` copy survives that move; the run-directory copy does not.
## Plain-text fallback ## Plain-text fallback
If a file's syntax-aware parsing produces noisy or incorrect results (an unsupported or malformed format), rerun with `--ignore-syntax` to treat it as plain text instead of relying on the format-specific parser. If a file's syntax-aware parsing produces noisy or incorrect results (an unsupported or malformed format), rerun with `--ignore-syntax` to treat it as plain text instead of relying on the format-specific parser.

View File

@@ -8,7 +8,7 @@ description: >
metadata: metadata:
category: lint category: lint
version: "0.1.1" version: "0.1.2"
source_keys: source_keys:
- context7-websites-vale-sh - context7-websites-vale-sh
- house-vale-3-15-2-repro - house-vale-3-15-2-repro
@@ -19,7 +19,7 @@ metadata:
- A style in `BasedOnStyles` that is neither built-in nor a directory under `StylesPath` fails hard, not silently: `E100 [loadStyles]`, exit 2, nothing linted. - A style in `BasedOnStyles` that is neither built-in nor a directory under `StylesPath` fails hard, not silently: `E100 [loadStyles]`, exit 2, nothing linted.
- `vale sync` alone does not clear that `E100`. Sync fetches only what the top-level `Packages` key declares, so against a `BasedOnStyles`-only name it reports `Synced 0 package(s)` and exits 0, fetching nothing. Add the style to `Packages`, then sync. A style lints only once it is in both keys — and the reverse case is silent, exiting 0. - `vale sync` alone does not clear that `E100`. Sync fetches only what the top-level `Packages` key declares, so against a `BasedOnStyles`-only name it reports `Synced 0 package(s)` and exits 0, fetching nothing. Add the style to `Packages`, then sync. A style lints only once it is in both keys — and the reverse case is silent, exiting 0.
- Only *package* styles need fetching: built-in `Vale`, and any style whose YAML is already committed under `StylesPath`, lint with no `Packages` entry and no sync. - Only *package* styles need fetching: built-in `Vale`, and any style whose YAML is already committed under `StylesPath`, lint with no `Packages` entry and no sync.
- `.vale.ini` is order-sensitive: core settings first, then `[formats]`, then glob sections. Anything written below a glob header applies only to files matching that glob. - `.vale.ini` order is enforced, not stylistic: put core settings first, then `[formats]`, then glob sections. A core setting (`StylesPath`, `MinAlertLevel`, `Vocab`, `IgnoredScopes`, `SkippedScopes`) written below a `[glob]` header is a hard error — `E201 ... 'StylesPath' is a core option; it should be defined above any syntax-specific options`, exit 2, nothing linted. `Packages` is the exception, and the worse one: below a glob header it is accepted with no error, then ignored — `vale sync` reports `Synced 0 package(s)` and downloads nothing.
- A rule scoped to `text.frontmatter.<key>` silently matches nothing when the field spans multiple lines in most YAML forms. If you scope a rule to frontmatter, read `references/configuration-reference.md` first. - A rule scoped to `text.frontmatter.<key>` silently matches nothing when the field spans multiple lines in most YAML forms. If you scope a rule to frontmatter, read `references/configuration-reference.md` first.
## Setup workflow ## Setup workflow
@@ -34,7 +34,7 @@ metadata:
[*.md] [*.md]
BasedOnStyles = Vale BasedOnStyles = Vale
``` ```
`Vale` here is the built-in style (`Vale.Spelling`, `Vale.Terms`, `Vale.Avoid`, `Vale.Repetition`) — no download needed, it always works. `Vale` here is the built-in style (`Vale.Spelling`, `Vale.Terms`, `Vale.Avoid`, `Vale.Repetition`): no `Packages` entry and no `vale sync`. It still needs the `StylesPath` directory to exist — declare `StylesPath = styles` without creating `styles/` and even a `Vale`-only config dies with `E201 ... The path '...' does not exist`, exit 2. That is why the previous step creates the directory.
- [ ] **Add third-party styles** (optional) by declaring them in `Packages`, then activating them in the same or another glob's `BasedOnStyles`: - [ ] **Add third-party styles** (optional) by declaring them in `Packages`, then activating them in the same or another glob's `BasedOnStyles`:
```ini ```ini
Packages = Google, write-good Packages = Google, write-good

View File

@@ -25,6 +25,10 @@ Map an unrecognized extension onto a supported one so Vale lints it with the rig
mdx = md 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. Install it with `npm install -g mdx2vast` or take the mapping.
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 ## Vocabularies
Reference a named vocabulary (a folder of accept/reject word lists under `StylesPath`) via `Vocab`, then apply styles per glob: Reference a named vocabulary (a folder of accept/reject word lists under `StylesPath`) via `Vocab`, then apply styles per glob:
@@ -89,6 +93,8 @@ Only *package* styles need fetching. A style whose YAML rule files are already c
| Style in `Packages` and synced, but in no glob's `BasedOnStyles` | 0 findings, exit 0 — downloads, never lints, indistinguishable from a clean run | | 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 | | `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 | | 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 ## Frontmatter Scopes

View File

@@ -11,7 +11,7 @@
## house-vale-3-15-2-repro ## house-vale-3-15-2-repro
- **URL:** (house-verified — reproduced locally against the `vale` binary, not an external source) - **URL:** (house-verified — reproduced locally against the `vale` binary, not an external source)
- **Description:** Behaviour of Vale 3.15.2 established by running it against purpose-built fixtures in this repo, where vale.sh documents nothing: the `E100 [loadStyles]` / exit-2 failure for a `BasedOnStyles` name absent from `StylesPath`, `vale sync` reporting `Synced 0 package(s)` for a name not declared in `Packages`, the `E201` / exit-2 failure when the `StylesPath` directory does not exist, the exit-0 no-op of an empty style directory, and the `text.frontmatter.<key>` scope matrix across multi-line YAML forms. - **Description:** Behaviour of Vale 3.15.2 established by running it against purpose-built fixtures in this repo, where vale.sh documents nothing: the `E100 [loadStyles]` / exit-2 failure for a `BasedOnStyles` name absent from `StylesPath`, `vale sync` reporting `Synced 0 package(s)` for a name not declared in `Packages`, the `E201` / exit-2 failure when the `StylesPath` directory does not exist, the exit-0 no-op of an empty style directory, the `E201` / exit-2 failure when a core option is written below a `[glob]` header (with `Packages` as the silent exception), and the `text.frontmatter.<key>` scope matrix across multi-line YAML forms.
- **Research doc:** none — house-verified reproduction, not part of the plugin's research corpus (no `plugins/lint/docs/research/` topic file backs this entry) - **Research doc:** none — house-verified reproduction, not part of the plugin's research corpus (no `plugins/lint/docs/research/` topic file backs this entry)
- **Contributing files:** SKILL.md, references/configuration-reference.md - **Contributing files:** SKILL.md, references/configuration-reference.md
- **Status:** `extracted` - **Status:** `extracted`

View File

@@ -6,16 +6,17 @@ description: >
as in "lint the docs", "check prose style", or "why is CI failing on the docs as in "lint the docs", "check prose style", or "why is CI failing on the docs
check". Not setting up Vale config or styles -> `vale-config`. check". Not setting up Vale config or styles -> `vale-config`.
metadata: metadata:
version: "0.1.2" version: "0.1.3"
category: lint category: lint
source_keys: source_keys:
- context7-websites-vale-sh - context7-websites-vale-sh
- house-vale-3-15-2-repro
--- ---
## Gotchas ## Gotchas
- Vale's exit code keys off `error`-level alerts only — `warning` and `suggestion` alerts print and still exit `0`, and `MinAlertLevel`/`--minAlertLevel` filter what is displayed, never the exit code — no flag makes warnings fail. A rule that must gate CI or a commit hook has to be `level: error`. This is the most common way a Vale gate silently passes everything. - Vale's exit code keys off `error`-level alerts only — `warning` and `suggestion` alerts print and still exit `0`, and `MinAlertLevel`/`--minAlertLevel` filter what is displayed, never the exit code — no flag makes warnings fail. A rule that must gate CI or a commit hook has to be `level: error`. This is the most common way a Vale gate silently passes everything.
- Check whether the target repo documents its own `vale` wrapper script (README, CONTRIBUTING, pre-commit config, `scripts/`) before calling the binary. Some projects wrap `vale` to work around real bugs — e.g. a scope that silently stops matching multi-line YAML block-scalar frontmatter — so bare `vale` skips whatever the wrapper fixes. Invoke the documented wrapper with the same arguments. - Check whether the target repo documents its own `vale` wrapper script (README, CONTRIBUTING, pre-commit config, `scripts/`) before calling the binary. Some projects wrap `vale` to work around real bugs — e.g. a `text.frontmatter.<key>` scope that silently stops matching multi-line values (`>` folded scalars, plain continuation lines and quoted multi-line scalars all go unmatched; a `|` literal block scalar still works) — so bare `vale` skips whatever the wrapper fixes. Invoke the documented wrapper with the same arguments.
## Running vale ## Running vale
@@ -34,20 +35,22 @@ Key flags:
| `--no-exit` | Suppresses the nonzero exit that `error`-level alerts would otherwise cause; a no-op when no rule is `error`-level. Use in CI stages that should surface lint output without hard-failing the build. | | `--no-exit` | Suppresses the nonzero exit that `error`-level alerts would otherwise cause; a no-op when no rule is `error`-level. Use in CI stages that should surface lint output without hard-failing the build. |
| `--ignore-syntax` | Treats input as plain text, skipping format-aware parsing — use when a file's syntax-aware parser produces noisy or wrong results. | | `--ignore-syntax` | Treats input as plain text, skipping format-aware parsing — use when a file's syntax-aware parser produces noisy or wrong results. |
`vale sync` downloads the packages/styles declared in `.vale.ini` — that's a one-time-per-change setup step (vale-config's territory), not part of a normal lint run. If a run behaves as though no styles are active, check `vale ls-config` to confirm what actually loaded before concluding it is a sync problem — an unrun `vale sync` is the usual cause, and not a `vale-run` problem. `vale sync` downloads the packages/styles declared in `.vale.ini` — that's a one-time-per-change setup step (vale-config's territory), not part of a normal lint run. If a run behaves as though no styles are active, check `vale ls-config` before concluding it is a sync problem — an unrun `vale sync` is the usual cause, and not a `vale-run` problem. `ls-config` resolves config files, styles and `StylesPath` search paths; it never enumerates rules, so it cannot tell you a given rule is live.
Prefer `--output=JSON` whenever the caller (a script, a CI step, another agent) needs to act on individual alerts rather than just get a pass/fail signal — `CLI` and `line` are for humans reading the terminal. Prefer `--output=JSON` whenever the caller (a script, a CI step, another agent) needs to act on individual alerts rather than just get a pass/fail signal — `CLI` and `line` are for humans reading the terminal.
## Fixing false positives ## Fixing false positives
Before writing any inline suppression markup (steps 2 and 3 below), read `references/troubleshooting.md` for your file's format: the markup is format-specific, and the wrong form suppresses nothing while Vale reports no error — the Markdown HTML-comment form is inert in an MDX file. Before writing any inline suppression markup (steps 2 and 3 below), read `references/troubleshooting.md`: the form follows the parser the config picks, not the file extension, and the wrong form suppresses nothing while Vale reports no error.
`.mdx` is the trap — the two parsers take opposite forms, so read `.vale.ini` first. Under `[formats] mdx = md` (what `vale-config` recommends) it is Markdown: `<!-- vale off -->` suppresses, `{/* vale off */}` does not. Without that mapping Vale takes the native MDX path, which needs the external `mdx2vast` binary (`npm install -g mdx2vast`); missing, the whole invocation dies — `E100 [lintMDX] ... mdx2vast not found`, exit 2 — leaving every other file in the run unlinted too.
Scope the fix as narrowly as possible, in this order: Scope the fix as narrowly as possible, in this order:
1. **Mentioning banned phrasing rather than using it**: wrap it in backticks or a fenced code block. Vale skips code spans and fences, so no suppression is needed at all. Try this before any suppression markup. 1. **Mentioning banned phrasing rather than using it**: wrap it in backticks or a fenced code block. Vale skips code spans and fences, so no suppression is needed at all. Try this before any suppression markup.
2. **One-off**: inline-suppress the specific text run with the format's `vale off`/`vale on` markup. 2. **One-off**: inline-suppress the specific text run with the format's `vale off`/`vale on` markup.
3. **Recurring known-exception string, one rule**: disable that specific rule for that specific match inline (in Markdown, e.g. `<!-- vale Style.Redundancy["ACT test","OTHER"] = NO -->` ... `= YES`), rather than the whole rule. 3. **Recurring known-exception string, one rule**: disable that specific rule for that specific match inline (in Markdown, e.g. `<!-- vale Style.Redundancy["ACT test","OTHER"] = NO -->` ... `= YES`), rather than the whole rule.
4. **Known project term failing spell check**: add it to the style's `ignore` list, not an inline suppression. 4. **Known project term failing spell check**: add it to the style's `ignore` list, not an inline suppression — and put the listed file at the `StylesPath` root, because an `ignore` path that resolves nowhere is a silent no-op (`references/troubleshooting.md`).
Never disable a rule project-wide to fix one false positive — editing `.vale.ini`/`BasedOnStyles` is vale-config's job, and it silences the rule everywhere, not just the false-positive case. Never disable a rule project-wide to fix one false positive — editing `.vale.ini`/`BasedOnStyles` is vale-config's job, and it silences the rule everywhere, not just the false-positive case.

View File

@@ -7,3 +7,11 @@
- **Research doc:** plugins/lint/docs/research/docs/vale/sources.md - **Research doc:** plugins/lint/docs/research/docs/vale/sources.md
- **Contributing files:** SKILL.md, references/troubleshooting.md - **Contributing files:** SKILL.md, references/troubleshooting.md
- **Status:** `extracted` - **Status:** `extracted`
## house-vale-3-15-2-repro
- **URL:** (house-verified — reproduced locally against the `vale` binary, not an external source)
- **Description:** Behaviour of Vale 3.15.2 established by running it against purpose-built fixtures in this repo, where vale.sh documents nothing or documents it wrongly: `.mdx` has no built-in support and needs either `[formats] mdx = md` or an external `mdx2vast` binary (absent, the whole invocation exits 2 with `E100 [lintMDX]`), the inline-suppression form inverts between those two configurations, the `spelling` check's `ignore` paths resolve against `StylesPath` or the working directory but never against the rule file's own directory and fail silently when they resolve nowhere, `ls-config` reports styles and paths but never rules, and the `text.frontmatter.<key>` scope matrix across multi-line YAML forms.
- **Research doc:** none — house-verified reproduction, not part of the plugin's research corpus (no `plugins/lint/docs/research/` topic file backs this entry)
- **Contributing files:** SKILL.md, references/troubleshooting.md
- **Status:** `extracted`

View File

@@ -1,6 +1,7 @@
--- ---
source_keys: source_keys:
- context7-websites-vale-sh - context7-websites-vale-sh
- house-vale-3-15-2-repro
--- ---
# Vale troubleshooting reference # Vale troubleshooting reference
@@ -9,19 +10,68 @@ source_keys:
`vale ls-config` prints the fully-resolved, currently active configuration as JSON. Check that `vale ls-config` prints the fully-resolved, currently active configuration as JSON. Check that
before rereading `.vale.ini` — what is written in the config file is not necessarily what is before rereading `.vale.ini` — what is written in the config file is not necessarily what is
active, and the resolved output is the fastest way to see which styles and rules a run actually active, and the resolved output is the fastest way to see which config files, styles and
loaded. `StylesPath` directories a run actually loaded.
It stops at styles. It does not enumerate rules, and neither does any other Vale 3.15.2
subcommand — `ls-config`, `ls-dirs`, `ls-vars` and `ls-metrics` were each checked and none
names the rule. Against a config
whose custom rule was demonstrably firing on the target file, `ls-config` reported
`"SBaseStyles": {"*.md": ["MyStyle"]}` and the two `StylesPath` search paths, but
`"Checks": null`, `"SChecks": {"*.md": {}}` and `"RuleToLevel": {}` — the firing rule's own name
appeared nowhere in the output. So `ls-config` answers "is this style loaded, and from where",
not "is this rule live". For the latter, run Vale over a small fixture that should trigger the
rule and see whether it alerts.
## Inline suppression syntax by format ## Inline suppression syntax by format
Markdown uses HTML comments — the MDX `{/* */}` form does not suppress anything in a plain `.md` file: ### Prerequisite: `.mdx` needs a decision before it lints at all
Vale 3.15.2 has no built-in MDX support. If `.vale.ini` does **not** map the extension, Vale takes
the native MDX path and shells out to an external `mdx2vast` binary. Without it on `PATH` the run
dies before linting anything:
```
$ vale . # doc.md and doc.mdx both present, no [formats] mapping
E100 [lintMDX] Runtime error
mdx2vast not found
Execution stopped with code 1.
$ echo $?
2
```
That is the whole invocation, not just the `.mdx` file — the `.md` alongside it produced no output
either. Fix it one of two ways, and the choice is not cosmetic because it also decides the
suppression syntax:
| `.vale.ini` | Prerequisite | Parser | Suppression form that works |
|---|---|---|---|
| `[formats]` maps `mdx = md` (what `vale-config` recommends) | none | Markdown | `<!-- vale off -->` |
| no `mdx` mapping (native MDX) | `npm install -g mdx2vast` | MDX | `{/* vale off */}` |
Key the markup to that config row, never to the file extension. Verified against Vale 3.15.2, same
three fixtures under each config:
| File | Mapped `mdx = md` | Native MDX (`mdx2vast` installed) |
|---|---|---|
| no suppression (control) | alert, exit 1 | alert, exit 1 |
| `<!-- vale off -->` | suppressed, exit 0 | `E100 ... failed to parse MDX: Unexpected character` `` `!` ``, exit 2 |
| `{/* vale off */}` | **not suppressed**, exit 1 | suppressed, exit 0 |
Under the mapping the JSX comment is worse than inert: it is linted as prose, so
`{/* vale Vale.Repetition = NO */}` produced three alerts where the un-suppressed file produced
one — its own markup tripped the rule twice more.
Markdown, and `.mdx` mapped onto it — HTML comments:
```markdown ```markdown
<!-- vale off --> <!-- vale off -->
This text will be ignored. This text will be ignored.
<!-- vale on --> <!-- vale on -->
``` ```
MDX: Native MDX only (no `[formats]` mapping, `mdx2vast` on `PATH`):
```mdx ```mdx
{/* vale off */} {/* vale off */}
This text will be ignored. This text will be ignored.
@@ -46,7 +96,8 @@ This is some text ACT test
<!-- vale Style.Redundancy["ACT test","OTHER"] = YES --> <!-- vale Style.Redundancy["ACT test","OTHER"] = YES -->
``` ```
MDX: Native MDX only — under `[formats] mdx = md` an `.mdx` file takes the Markdown form above, and
this one silences nothing:
```mdx ```mdx
{/* vale Style.Redundancy["ACT test","OTHER"] = NO */} {/* vale Style.Redundancy["ACT test","OTHER"] = NO */}
This is some text ACT test This is some text ACT test
@@ -66,6 +117,36 @@ ignore:
- ignore2.txt - ignore2.txt
``` ```
**Where the file goes, and why a wrong answer is invisible.** Each entry resolves against the
`StylesPath` root, or against the working directory `vale` is invoked from. It does **not** resolve
against the rule file's own directory — which is the natural reading of the YAML above, since the
path sits inside the rule, and it is wrong. Verified against Vale 3.15.2 across four fresh trees,
each with the same rule and the same unknown word:
| Where `ignore1.txt` was placed | Result |
|---|---|
| `<StylesPath>/ignore1.txt` | word ignored, exit 0 |
| `./ignore1.txt` in the directory `vale` runs from | word ignored, exit 0 |
| `<StylesPath>/<Style>/ignore1.txt`, beside the rule | word still flagged, exit 1 |
| file absent entirely | word still flagged, exit 1 |
The two failing rows emit no warning, no error, and no diagnostic of any kind: the output is
byte-identical to the same rule with the `ignore` key deleted. A misplaced ignore list looks exactly
like a list that was read and did not contain the word.
Prefer the `StylesPath` root. The run-directory form is the fragile one — move the invocation and it
silently stops working. Same tree, same file, only the working directory changed:
```
$ cd project && vale doc.md # ignore1.txt at project root
✔ 0 errors, 0 warnings and 0 suggestions in 1 file. # exit 0
$ cd /elsewhere && vale --config=project/.vale.ini project/doc.md
1:5 error Did you really mean 'zzqwidget'? MyStyle.Spell # exit 1
```
The `StylesPath` copy survives that move; the run-directory copy does not.
## Plain-text fallback ## Plain-text fallback
If a file's syntax-aware parsing produces noisy or incorrect results (an unsupported or malformed format), rerun with `--ignore-syntax` to treat it as plain text instead of relying on the format-specific parser. If a file's syntax-aware parsing produces noisy or incorrect results (an unsupported or malformed format), rerun with `--ignore-syntax` to treat it as plain text instead of relying on the format-specific parser.