Skip to content

One large markdown file blocks the whole Vale run, because Vale is quadratic in file size #321

Description

@thecodedrift

check abandons every file's findings when a single markdown file is large enough to exceed the Vale timeout. It is the same shape #300 fixed for unparseable files, on a path #300 did not cover, and the size threshold is far lower than it looks because Vale is quadratic.

Found while investigating a flaky test (#262 follow-up, #320), not in the field. Nobody has hit it yet that we know of.

Vale is quadratic in the size of ONE file

Measured against the pinned binary (@taskless/vale-darwin-arm64@3.19.0-...), one existence rule, one file, prose repeated to size:

size time linear would be
40KB 0.077s
80KB 0.30s 0.15s
160KB 0.91s 0.31s
320KB 3.50s 0.62s
640KB 14.3s 1.23s
1MB 48.5s 2.0s

Each doubling costs roughly 4x. This is upstream Vale's behaviour, not ours.

It is worth stressing that this is per-FILE, not per-corpus. The same ~1MB of prose spread across 400 files takes 190ms, versus 48s in one file. Volume is fine; size is not.

What our timeout does with that

VALE_TIMEOUT_MS is 60_000 (packages/cli/src/rules/vale/run.ts:47), and a timeout returns blocking: true and returns immediately:

return { status: "timeout", blocking: true, message: attempt.message };

It is NOT excluded-and-retried the way an unparseable file now is. The retry loop added for #300 narrows the run on a config error naming a target file; a timeout has no such path.

So one file over roughly 1.1MB takes Vale past 60s, and the entire run is blocked. Every other file's findings are lost, which is precisely the complaint in #300: "a checker's silence stops meaning anything."

Why the threshold is lower in practice

The 1.1MB figure is for a single rule on one machine. Real projects have several rules, slower or contended hardware, and CI runners. The quadratic means the margin does not degrade gently: 640KB is already 14 seconds, so a project can sit well inside the timeout while being one long note away from falling off it, and the failure arrives as a whole-run block rather than as a slow file.

Markdown of that size is unusual but not exotic: a generated changelog, an exported vault note, a data table pasted into prose. #300 was found on a 328-file markdown vault.

Suggested direction

Not obvious, and worth deciding rather than patching:

  • Treat a timeout like a parse error where possible. Vale's timeout gives us no path, so there is nothing to exclude on the retry. Running per-file to attribute it would undo the batching the current design relies on.
  • Report partial results instead of abandoning them. If a timeout kills a batch, findings already parsed from stdout could still be returned alongside a blocking notice naming what was not checked. That keeps one slow file from costing every other file's findings, which is the actual harm.
  • Reconsider whether a timeout must be blocking at all. It is blocking today so a silently disabled engine cannot read as a clean pass, which is the right instinct. A partial result plus an explicit "these files were not checked" notice may serve that better than discarding everything.
  • A per-file size guard that reports an oversized file as its own finding, the way an unparseable one now is, rather than letting it consume the batch's whole budget.

Repro

  1. A project with one Vale rule.
  2. A single .md file of ~1.2MB of prose.
  3. npx @taskless/cli check . --json
  4. The run blocks on the timeout; results carries nothing for any other file.

The measurements above are reproducible with the binary directly, which is the faster way to see the curve.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIRelated to the taskless CLIbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions