Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .changeset/vale-converter-formats.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
"@taskless/cli": patch
---

Stop one AsciiDoc file from disabling every Vale rule in the project.

Vale supports AsciiDoc, reStructuredText, XML/DITA and MDX, but it parses none
of them by itself — it shells out to `asciidoctor`, `rst2html`, `dita` or
`mdx2vast`, and the `@taskless/vale-*` packages ship the binary with none of
those alongside it. On a host without the converter Vale does not skip the file:
it prints one `E100 [lintAdoc] Runtime error` on stderr, writes nothing at all to
stdout, and exits 2. The abort is Vale's own and it is not scoped to the file
that caused it, so every finding from every other file in the run was destroyed
before it was ever serialized. Measured against the example project, adding a
single `.adoc` took a check that reported five Vale findings across four files
down to zero — reported as a raw JSON blob among the results, and exiting 1 the
same way any ordinary failing check does.

`runVale` now excludes the converter-dependent extensions from Vale's own walk,
so the rest of the project is checked normally and the skipped files are named
in a notice that says which converter would put them back in scope. The tiers
live in one table in `rules/capabilities.ts` — the same record the agent recipes
render their format lists from — measured against the pinned binary rather than
transcribed from documentation. That is how `.asc` and `.rest`, a third AsciiDoc
spelling and a second reStructuredText one that crash identically and were in
neither bug report, ended up covered. Measurement also corrected four
extensions that a documentation reading had put in the wrong tier: `.tex`,
`.rmd`, `.mkd` and `.mkdn` are all read as plain text by this Vale, not parsed,
so excluding them would have dropped files Vale lints perfectly well. A
per-extension test re-measures every row against the real Vale — each tier by
the property only that tier has, since ordinary prose fires in all of them — so
a version bump that moves a format between tiers fails there instead of silently
turning the engine off again.

The recipes now say MDX is not supported _yet_, rather than unsupported: Vale
3.18.0 parses it natively and a CLI update carrying that Vale is expected to
bring it. The same release adds a Typst converter, which will move `.typ` out of
the plaintext tier, so the table carries a standing instruction to re-measure
every row on a version bump.

Two details are load-bearing and were both wrong on the first attempt. Vale
honours exactly one `--glob` and keeps the last, so the `.taskless/` exclusion
and the format exclusions have to travel as one negated alternation or the first
is silently discarded. And Vale matches a `--glob` against the basename only
when the pattern contains no `/` — combined with `.taskless/**` the whole
expression goes path-wise, at which point a bare `*.adoc` stops matching
`docs/guide.adoc` and the crash survives one directory down from wherever it was
tested. Vale's error output is also decoded now rather than forwarded verbatim,
so a failure reads as a sentence naming the missing program instead of a
five-field JSON object.

**These formats are now stated as unsupported rather than as needing a tool.**
The notice used to end "Install it and put it on your PATH to have these files
checked", which offered a path this build does not ship, does not test, and for
`.xml` cannot deliver — an XSLT stylesheet is specific to the document, so no
install makes it lintable. It also made behaviour host-dependent: macOS ships
`/usr/bin/xsltproc` and typical Linux CI images do not, so the same repository
checked differently depending on the machine. The exclusion is unconditional
for that reason, and the programs are still named as the reason rather than as
a remedy.

The comment tier was reconciled against Vale's own documentation at
docs.vale.sh/formats/code, which adds `.bsh`, `.csx`, `.pod`, `.py3` and `.sbt`
once measured. It also documents `.pyi`, `.qml` and `.scss` as comment-aware,
and on the pinned 3.17.1 a bare non-comment line in each of them lints — so they
stay in the plaintext tier. That divergence is the argument for probing rather
than transcribing: the docs describe the current Vale, this build pins an older
one, and copying the list would have shipped `.scss` as comment-aware and been
wrong. `.pod` is a reminder of how easily this is misread — it lints Perl
comments but not POD blocks, so probing it with `=head1` looks like no support
at all.
20 changes: 17 additions & 3 deletions packages/cli/src/agent/create-vale-rule.txt
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,32 @@ it.
parser, so the whole file is linted as prose: a rule matched to
YAML flags key names and values, not just the comments. If that is
not what the rule means, narrow the glob rather than accepting it.
- **cannot be read at all** — Vale supports these formats but shells
out to an external converter to parse them, and this CLI ships none
of those converters:
These land here despite reading like markup, so a `scope:` value
has nothing to act on in them: %(VALE_PLAINTEXT_FORMATS)s
- **not supported** — Vale parses these only by shelling out to an
external program, and this build does not support any format that
needs one:
%(VALE_CONVERTER_FORMATS)s

Do not tell the user to install the program. Taskless excludes these
files from the run whatever is installed, so that a repository
checks the same way on every machine; `.xml` could not work anyway,
since an XSLT stylesheet is specific to the document.

**A single unreadable file fails the whole Vale pass.** Vale exits 2
with an `E100` runtime error and abandons the run — `--no-exit` does
not suppress it — so every other Vale rule over every other file goes
unreported. `[*.{md,mdx}]` is not a slightly wider `[*.md]`; it is a
matcher that takes `check` down the first time the repo grows an
`.mdx` file. Never put one of those extensions in a glob.

**MDX is not supported yet.** Vale v%(VALE_VERSION)s reads `.mdx` only
through `mdx2vast`, which this CLI does not ship. Vale 3.18.0 parses
MDX natively, and a CLI update carrying that Vale is expected to
bring MDX support with it. Until then, scope the rule to `.md` and
say that `.mdx` is not supported yet rather than offering a matcher
that covers it.

5. **Write the fixtures.** Two directories inside the rule, both flat.
Vale lints the whole fixture tree, so a document nested a level
deeper would be linted and never checked against either bucket, which
Expand Down
19 changes: 15 additions & 4 deletions packages/cli/src/agent/route.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,29 @@ answered together.
`.sql` and every unnamed extension included. There is no parser,
so the file is linted as one block of prose, and a Vale rule
scoped to YAML flags the code as readily as the comments. That is
rarely what was asked for — say so before writing it.
- *cannot be read at all* — Vale supports these formats but shells
out to an external converter to parse them, and this CLI ships
none of those converters:
rarely what was asked for — say so before writing it. These read
like markup and are not: %(VALE_PLAINTEXT_FORMATS)s
- *not supported* — Vale parses these only by shelling out to an
external program, and this build does not support any format that
needs one:
%(VALE_CONVERTER_FORMATS)s

Installing the program does not change this. Taskless excludes
these files from the Vale run whatever is on the machine, so that
a repository checks the same way everywhere rather than depending
on what a given host happens to have available.

**One such file fails the entire Vale pass, not just that file.**
Vale exits 2 with an `E100` runtime error, `--no-exit` does not
suppress it, and every other Vale rule over every other file goes
unreported. A matcher written as `[*.{md,mdx}]` is not a wider
`[*.md]` — it is a broken one.

**MDX is not supported yet.** Vale 3.18.0 parses MDX natively and
a CLI update carrying that Vale is expected to bring it; on
v%(VALE_VERSION)s, `.mdx` needs `mdx2vast` and this CLI ships
none. Tell the user MDX is not supported yet and scope to `.md`.

**A language on neither list does not route to runtime by default.**
Check `create-legacy-rule` first: the repo may already run a linter
that speaks it, and that linter's own dialect is a local destination
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/prompts/recipes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
valeCommentList,
valeConverterList,
valeMarkupList,
valePlaintextList,
} from "../rules/capabilities";

// Agent recipe files embedded at build time via Vite import.meta.glob.
Expand Down Expand Up @@ -164,6 +165,7 @@ export function buildVariables(
VALE_VERSION,
VALE_MARKUP_FORMATS: valeMarkupList(),
VALE_COMMENT_FORMATS: valeCommentList(),
VALE_PLAINTEXT_FORMATS: valePlaintextList(),
VALE_CONVERTER_FORMATS: valeConverterList(),
PACKAGE_MANAGER_DLX:
options.packageManagerDlx ?? PACKAGE_MANAGER_DLX_MARKER,
Expand Down
Loading