Skip to content

Commit dc2b88c

Browse files
theCodeDriftclaude
andcommitted
docs(cli): retire the 3.18.0 forecast, and measure the MyST row it predicted
The format table's own header insists on "MEASURED, NOT DOCUMENTED", and the bump left its prose describing a version we no longer pin: the block above VALE_FORMAT_TIERS still framed 3.18.0 as "the known incoming bump", the plaintext bullet still cited `.rmd`, the `.pyi` note still said "we pin 3.17.1", and VALE_CONVERTER_DEPENDENT still used `[*.{md,mdx}]` as the glob that takes down the whole pass — the one place the `.typ` swap was missed. The same `.rmd` staleness sat in two contract-test doc comments. The forecast also predicted a MyST row and none was added. Probed on the pinned binary, `.myst` skips a fenced code block (zero findings) where a bare line lints, so it is markup; it is now a row with its own fixture. The 3.18.0 changeset is folded into `.changeset/vale-converter-formats.md` rather than shipping beside it, per "a later PR extends the existing file" — and folding it caught two sentences there that the bump falsified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cyga14bww8rmazH2XrF8ms
1 parent 9bede55 commit dc2b88c

5 files changed

Lines changed: 89 additions & 78 deletions

File tree

‎.changeset/vale-3-18-0.md‎

Lines changed: 0 additions & 30 deletions
This file was deleted.

‎.changeset/vale-converter-formats.md‎

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,26 @@ the property only that tier has, since ordinary prose fires in all of them — s
3232
a version bump that moves a format between tiers fails there instead of silently
3333
turning the engine off again.
3434

35-
The recipes now say MDX is not supported _yet_, rather than unsupported: Vale
36-
3.18.0 parses it natively and a CLI update carrying that Vale is expected to
37-
bring it. The same release adds a Typst converter, which will move `.typ` out of
38-
the plaintext tier, so the table carries a standing instruction to re-measure
39-
every row on a version bump.
35+
The engine moves to Vale 3.18.0 in the same release, and the table carries a
36+
standing instruction to re-measure every row on a version bump — so every row
37+
was re-probed against the new binary rather than carried over. Eight moved.
38+
`.mdx` gains a native parser and leaves the unsupported tier, so a matcher like
39+
`[*.{md,mdx}]` — the worked example above — is legitimate again, and `[*.{md,typ}]`
40+
takes its place as the broken one. `.typ` moved the opposite way: Typst now
41+
parses through `typst2vast`, an external program this build does not ship, so a
42+
Typst file is excluded from the run rather than read as prose the way 3.17.1
43+
read it. `.rmd` and the new `.qmd` and `.myst` are parsed as markup, and `.qml`,
44+
`.scss` and the new `.qdoc` are comment-aware where they previously fell through
45+
to plain text.
46+
47+
The `.typ` move is the one that mattered to get right. An extension missing from
48+
the table is read as prose, which is harmless — but the moment Vale routes it to
49+
a converter, that same omission is a crash that takes down every Vale rule in
50+
the run. Bumping the binary without re-measuring would have introduced exactly
51+
the failure this table exists to prevent, under an extension nobody was
52+
watching. Re-probing also caught one change the release notes do not mention:
53+
PHP comment extraction now requires a real `<?php` tag, where 3.17.1 linted a
54+
bare `//` comment without one.
4055

4156
Two details are load-bearing and were both wrong on the first attempt. Vale
4257
honours exactly one `--glob` and keeps the last, so the `.taskless/` exclusion
@@ -62,10 +77,10 @@ a remedy.
6277
The comment tier was reconciled against Vale's own documentation at
6378
docs.vale.sh/formats/code, which adds `.bsh`, `.csx`, `.pod`, `.py3` and `.sbt`
6479
once measured. It also documents `.pyi`, `.qml` and `.scss` as comment-aware,
65-
and on the pinned 3.17.1 a bare non-comment line in each of them lints — so they
66-
stay in the plaintext tier. That divergence is the argument for probing rather
67-
than transcribing: the docs describe the current Vale, this build pins an older
68-
one, and copying the list would have shipped `.scss` as comment-aware and been
69-
wrong. `.pod` is a reminder of how easily this is misread — it lints Perl
80+
and on 3.17.1 a bare non-comment line in each of them lints. 3.18.0 makes the
81+
claim true for `.qml` and `.scss` and still not for `.pyi`, which stays in the
82+
plaintext tier. That divergence is the argument for probing rather than
83+
transcribing: the docs describe whatever Vale is current, and copying the list
84+
would have shipped `.pyi` as comment-aware and been wrong for both builds. `.pod` is a reminder of how easily this is misread — it lints Perl
7085
comments but not POD blocks, so probing it with `=head1` looks like no support
7186
at all.

‎packages/cli/src/rules/capabilities.ts‎

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ const CONVERTER_TIER_PREFIX = "converter:";
139139
* on a bare non-comment line yields ZERO. A type that fires on both is the
140140
* plaintext fallback wearing a code extension.
141141
* - **plaintext** — a bare line yields a finding. Listed only where the tier is
142-
* surprising: `.tex`, `.rmd`, `.mkd` and `.mkdn` all look like markup and are
143-
* not. Everything unnamed lands here too, which is why this tier does not
144-
* need to be exhaustive.
142+
* surprising: `.tex`, `.mkd` and `.mkdn` all look like markup and are not.
143+
* Everything unnamed lands here too, which is why this tier does not need to
144+
* be exhaustive.
145145
* - **converter** — a non-zero exit whose output carries `E100` and the
146146
* program's name.
147147
*
@@ -158,22 +158,33 @@ const CONVERTER_TIER_PREFIX = "converter:";
158158
* plain text today, but the moment Vale routes it to a converter the same
159159
* omission is a crash that takes down every Vale rule in the run.
160160
*
161-
* Vale 3.18.0 is the known incoming bump, and it moves rows in three different
162-
* directions — which is why "re-measure" is not boilerplate here:
161+
* The 3.17.1 → 3.18.0 bump is what that warning looks like in practice. Every
162+
* row below was re-probed against the 3.18.0 binary, and eight moved, in three
163+
* different directions — which is why "re-measure" is not boilerplate here:
163164
*
164-
* - `.mdx` gains a native parser, so it moves `converter:mdx2vast` → `markup`
165-
* and becomes supported.
166-
* - `.typ` gains a parser that shells out to `typst2vast`
167-
* (https://docs.vale.sh/formats/typst), so it moves `plaintext` →
168-
* `converter:typst2vast`. That is the dangerous direction: today it is read
169-
* as prose, and after the bump the same row would crash the run. It also
170-
* stays unsupported permanently, since we do not support formats needing an
171-
* external program.
172-
* - MyST, Quarto and QDoc arrive with parsers needing no external program, so
173-
* they become genuinely supportable and want `markup` rows once measured.
165+
* - `.mdx` gained a native parser: `converter:mdx2vast` → `markup`. It is
166+
* supported now, and `[*.{md,mdx}]` is a legitimate matcher again.
167+
* - `.typ` gained a parser that shells out to `typst2vast`
168+
* (https://docs.vale.sh/formats/typst): `plaintext` →
169+
* `converter:typst2vast`. That is the dangerous direction — 3.17.1 read it as
170+
* prose, and the same row on 3.18.0 crashes the run. It stays unsupported
171+
* permanently, since we do not support formats needing an external program.
172+
* - `.rmd` gained a real Markdown parser, so it left the "looks like markup and
173+
* is not" list above: `plaintext` → `markup`.
174+
* - `.qml` and `.scss` gained parsers that see their comments: `plaintext` →
175+
* `comment`. Vale's docs had claimed both for years; on 3.17.1 the claim was
176+
* measurably false and on 3.18.0 it is true.
177+
* - `.qmd` (Quarto) and `.myst` (MyST) measured as `markup`, and `.qdoc` as
178+
* `comment` — QDoc documentation lives in a doc-comment block, so it is
179+
* comment extraction rather than the markup tier a first reading of the
180+
* release notes suggests, and probing it with bare prose reads as no support
181+
* at all. All three are new rows, none of them reachable on 3.17.1.
174182
*
175-
* All four are documented as requiring v3.18.0 or later, so none of them is
176-
* reachable from {@link VALE_VERSION}. Re-probe every row on the bump.
183+
* PHP also changed without the release notes saying so: comment extraction now
184+
* needs a real `<?php` tag, where 3.17.1 linted a bare `//` comment without
185+
* one. The tier did not move, but the probe had to. Re-probe every row on the
186+
* next bump, by the discriminating property and by each language's own comment
187+
* syntax — a wrong delimiter reads exactly like absent support.
177188
*/
178189
export const VALE_FORMAT_TIERS: Readonly<Record<string, ValeFormatTier>> = {
179190
// markup — parsed, the format's own constructs skipped
@@ -183,6 +194,7 @@ export const VALE_FORMAT_TIERS: Readonly<Record<string, ValeFormatTier>> = {
183194
".md": "markup",
184195
".mdx": "markup",
185196
".mdown": "markup",
197+
".myst": "markup",
186198
".org": "markup",
187199
".qmd": "markup",
188200
".rmd": "markup",
@@ -235,11 +247,12 @@ export const VALE_FORMAT_TIERS: Readonly<Record<string, ValeFormatTier>> = {
235247
".mkd": "plaintext",
236248
".mkdn": "plaintext",
237249
".tex": "plaintext",
238-
// plaintext HERE, though Vale's own docs list them as comment-tier. The docs
239-
// describe the CURRENT Vale; we pin 3.17.1. Measured on the pinned binary a
240-
// bare non-comment line lints, which is the plaintext signature. Transcribing
241-
// the docs would have shipped these as comment-tier and been wrong for this
242-
// build — the case for probing rather than copying.
250+
// plaintext HERE, though Vale's own docs list it as comment-tier. Measured on
251+
// the pinned 3.18.0 binary a bare non-comment line lints, which is the
252+
// plaintext signature. `.qml` and `.scss` sat here for the same reason until
253+
// 3.18.0 made the docs true for them; `.pyi` is the row where transcribing
254+
// the docs would still ship the wrong tier — the case for probing rather than
255+
// copying.
243256
".pyi": "plaintext",
244257
// converter-dependent — Vale supports the format, we ship no converter
245258
".adoc": "converter:asciidoctor",
@@ -339,10 +352,12 @@ function groupByConverter(): ValeConverterFormat[] {
339352
*
340353
* The blast radius is what makes this worth surfacing at routing time rather
341354
* than at authoring time: Vale exits 2 with an `E100` runtime error and
342-
* abandons the run, and `--no-exit` does not suppress it. One `.mdx` file
355+
* abandons the run, and `--no-exit` does not suppress it. One `.typ` file
343356
* caught by a rule's glob takes down the entire Vale pass, including every
344-
* other rule and every other file — so `[*.{md,mdx}]` is not a slightly wider
345-
* matcher than `[*.md]`, it is a broken one.
357+
* other rule and every other file — so `[*.{md,typ}]` is not a slightly wider
358+
* matcher than `[*.md]`, it is a broken one. (`[*.{md,mdx}]` was that example
359+
* until 3.18.0 gave MDX a native parser — the membership of this tier is a
360+
* property of {@link VALE_VERSION}, and so is the worked example.)
346361
*/
347362
export const VALE_CONVERTER_DEPENDENT: readonly ValeConverterFormat[] =
348363
groupByConverter();

‎packages/cli/test/recipe-cross-references.test.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,11 @@ describe("recipes state engine reach from the pinned versions", () => {
385385
// author `.mdx` is unsupported costs them a format Vale reads fine, and
386386
// telling them `.typ` is readable takes their whole Vale pass down.
387387
expect(route).toContain("`.mdx` is supported");
388-
expect(route).toContain("`.typ`");
388+
// `.typ` alone would pass on the converter list rendered above, which names
389+
// it whatever the prose says. Pin the hazard sentence instead, so dropping
390+
// the warning fails here even while the extension is still mentioned.
391+
expect(route).toContain("`.typ` moved the other way");
392+
expect(route).toContain("`[*.{md,typ}]`");
389393
});
390394

391395
it("names ast-grep's languages in rendered create-sg-rule.txt", async () => {

‎packages/cli/test/vale-vendor-contract.test.ts‎

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,10 @@ withVale("Vale vendor contract", () => {
388388
* - **plaintext** is the tier that needs no separating — a bare line fires —
389389
* but the entries listed in it do: each names a construct a parser WOULD have
390390
* skipped, and the probe asserts Vale lints it. That is the assertion that
391-
* `.tex` and `.rmd` are not markup, and it is the one the first hand-written
392-
* table got backwards.
391+
* `.tex`, `.mkd` and `.mkdn` are not markup, and it is the one the first
392+
* hand-written table got backwards. It is also the assertion that expires: on
393+
* 3.17.1 `.rmd` belonged here, and 3.18.0 gave it a real parser and moved it
394+
* into MARKUP_FIXTURES.
393395
* - **converter-dependent** is separated from everything by failing.
394396
*
395397
* See taskless/cli#151.
@@ -467,12 +469,17 @@ withVale("Vale engine capabilities", () => {
467469
skipped: "Fine.\n\n```\nsimply\n```\n",
468470
},
469471
".org": { prose: "We simply do it.\n", skipped: "# simply\nFine.\n" },
470-
// Native as of 3.18.0. `.mdx` arrived from the converter tier and `.rmd`
471-
// and `.qmd` from plaintext, so all three are new claims, not carried over.
472+
// Native as of 3.18.0. `.mdx` arrived from the converter tier, `.rmd` from
473+
// plaintext, and `.qmd` and `.myst` are new rows — four new claims, none of
474+
// them carried over from the 3.17.1 table.
472475
".mdx": {
473476
prose: "We simply do it.\n",
474477
skipped: "Fine.\n\n```\nsimply\n```\n",
475478
},
479+
".myst": {
480+
prose: "We simply do it.\n",
481+
skipped: "Fine.\n\n```\nsimply\n```\n",
482+
},
476483
".qmd": {
477484
prose: "We simply do it.\n",
478485
skipped: "Fine.\n\n```{r}\nsimply <- 1\n```\n",
@@ -498,11 +505,10 @@ withVale("Vale engine capabilities", () => {
498505
it("reports the pinned version", () => {
499506
// VALE_VERSION is rendered beside the reach lists in route.txt and
500507
// create-vale-rule.txt, so it is the attribution for every claim below.
501-
// It also gates two of them, in opposite directions: Vale 3.18.0 parses MDX
502-
// natively, so a bump past it moves `.mdx` from converter-dependent to
503-
// markup — and the same release adds a Typst converter, so it moves `.typ`
504-
// from plaintext to `converter:typst2vast`. A bump re-measures the whole
505-
// table; these two are only the rows already known to move.
508+
// 3.18.0 moved rows in both directions: `.mdx` gained a native parser and
509+
// left the converter tier, while `.typ` gained a `typst2vast` converter and
510+
// entered it. A bump re-measures the whole table — the fixtures below are
511+
// the record of the last time that was done, not a forecast of the next.
506512
const result = spawnSync(binary as string, ["--version"], {
507513
encoding: "utf8",
508514
});
@@ -564,7 +570,7 @@ withVale("Vale engine capabilities", () => {
564570
* its spelling suggests would have skipped.
565571
*
566572
* Vale lints it, which is the whole finding: `.tex` is not TeX to Vale and
567-
* `.rmd` is not R Markdown. The mirror image of the markup fixtures — same
573+
* `.mkd` is not Markdown. The mirror image of the markup fixtures — same
568574
* documents, opposite expectation.
569575
*/
570576
const PLAINTEXT_FIXTURES: Record<string, string> = {
@@ -573,8 +579,9 @@ withVale("Vale engine capabilities", () => {
573579
".tex": "% simply in a comment\nFine.\n",
574580
// Documented by Vale as comment-aware, measured as plaintext on the pinned
575581
// binary — the construct here is the comment a parser would have skipped.
576-
// `.qml` and `.scss` were here until 3.18.0 gave them real parsers, which
577-
// is why the tier is re-measured on every bump rather than carried over.
582+
// `.qml` and `.scss` were here until 3.18.0 gave them real parsers, and
583+
// `.rmd` left for MARKUP_FIXTURES in the same bump, which is why the tier is
584+
// re-measured on every bump rather than carried over.
578585
".pyi": "# simply\nFine.\n",
579586
};
580587

0 commit comments

Comments
 (0)