The problem
Two checks read the prose today, each through a narrow slit. codespell catches a misspelled word. tools/sentence_complexity_check.py catches a sentence that needs a second pass. Neither reads style: a term the project spells two ways, a heading that answers a different question than its section asks, a sentence that buries its actor. That is what a prose linter is for, and of the tools this project shares with Quality-time, vale is the one it doesn't run.
The prose here is not incidental. The README is generated from docs/README.md.in and is the documentation; the CLI help is read by everyone who runs update-time -h; the log messages are the product's whole output. A defect in any of them reaches a user the way a defect in the code does.
The work
Add a vale check to just check, in the shape every other check has: a private recipe whose output is captured, reporting one PASS or FAIL line, joined to the parallel check recipe.
Vale is packaged on PyPI as vale (3.13.0.0 at the time of writing), which installs the binary into the environment, so it joins [dependency-groups] dev and arrives with uv sync like every other check rather than needing an install route of its own.
The increment ends with the check green over whatever it is pointed at, which may mean narrowing the style rather than rewriting the tree.
What this must not change
codespell and tools/sentence_complexity_check.py keep running, unless the work shows vale covers one of them outright.
just check stays parallel, and each check keeps reporting a single line.
- The docstring conventions in
.claude/CLAUDE.md. Where a packaged style contradicts them, the style is what gives way, since those conventions are chosen and the style is a default.
Out of scope
- Rewriting the prose that a wider style would flag. Which prose is worth rewriting is a question per finding, not a precondition for having the check.
- The generated
README.md, which is written from docs/README.md.in and would be checked twice.
Documentation
None for the README, which documents what Update-time does rather than how it is checked. The justfile's check recipe lists the checks, so vale joins that list.
Open questions
- What should it read? The other prose checks read
src tests tools docs *.md .claude/CLAUDE.md. A style linter earns most on the reader-facing prose — docs/README.md.in, CHANGELOG.md, and the CLI help in src — and least on docstrings, which follow this project's own rules and would argue with a packaged style.
- Which style: one vale packages, or a project style listing only the rules this project wants? A packaged style decides the first question too, since it is written for documentation rather than for docstrings.
- Does vale's readability rule cover what
tools/sentence_complexity_check.py does? That tool exists because a sentence needing a second pass is a defect no linter catches; if vale catches it, one of the two should go.
The problem
Two checks read the prose today, each through a narrow slit.
codespellcatches a misspelled word.tools/sentence_complexity_check.pycatches a sentence that needs a second pass. Neither reads style: a term the project spells two ways, a heading that answers a different question than its section asks, a sentence that buries its actor. That is what a prose linter is for, and of the tools this project shares with Quality-time, vale is the one it doesn't run.The prose here is not incidental. The README is generated from
docs/README.md.inand is the documentation; the CLI help is read by everyone who runsupdate-time -h; the log messages are the product's whole output. A defect in any of them reaches a user the way a defect in the code does.The work
Add a
valecheck tojust check, in the shape every other check has: a private recipe whose output is captured, reporting onePASSorFAILline, joined to the parallelcheckrecipe.Vale is packaged on PyPI as
vale(3.13.0.0 at the time of writing), which installs the binary into the environment, so it joins[dependency-groups] devand arrives withuv synclike every other check rather than needing an install route of its own.The increment ends with the check green over whatever it is pointed at, which may mean narrowing the style rather than rewriting the tree.
What this must not change
codespellandtools/sentence_complexity_check.pykeep running, unless the work shows vale covers one of them outright.just checkstays parallel, and each check keeps reporting a single line..claude/CLAUDE.md. Where a packaged style contradicts them, the style is what gives way, since those conventions are chosen and the style is a default.Out of scope
README.md, which is written fromdocs/README.md.inand would be checked twice.Documentation
None for the README, which documents what Update-time does rather than how it is checked. The justfile's
checkrecipe lists the checks, so vale joins that list.Open questions
src tests tools docs *.md .claude/CLAUDE.md. A style linter earns most on the reader-facing prose —docs/README.md.in,CHANGELOG.md, and the CLI help insrc— and least on docstrings, which follow this project's own rules and would argue with a packaged style.tools/sentence_complexity_check.pydoes? That tool exists because a sentence needing a second pass is a defect no linter catches; if vale catches it, one of the two should go.