Getting started
chaff finds the places in a text that are hard to read. It never rewrites anything; fixing is the writer’s job. Start by running it once on something you wrote.
npx chaffjs article.md
To try it before installing anything, paste a text into the playground, which runs chaff in your browser (Checking in a browser).
Pick the kind of document
A contract’s sentences run longer than a blog’s, a poem repeats its words, and a transcript keeps what was said. Each is how that kind of document is written. Pick the kind of document (the genre) and chaff sets its rules and limits to that kind, with no settings to write.
npx chaffjs --genre legal/contract contract.md check it as a contract, this run only
npx chaffjs init --genre legal/contract write the genre into this folder's chaff.yaml
npx chaffjs genres list the genres and what each is for
| Document | Genre |
|---|---|
| Specifications, RFCs, design documents | technical/spec |
| READMEs, documentation for developers | technical/readme |
| Technical articles (the genre when none is set) | blog/tech |
| Personal essays on a blog | blog/essay |
| Articles an organisation publishes | blog/owned-media |
| Proposals, plans, pitches | business/proposal |
| Reports, white papers, internal documents | business/report |
| Work email and letters | business/email |
| Press releases and public notices | business/press-release |
| Minutes and meeting notes | business/meeting-notes |
| Contracts, terms of service, privacy policies | legal/contract |
| Statutes, regulations, internal rules | legal/statute |
| Court judgments and opinions | legal/judgment |
| Patent specifications and claims | legal/patent |
| User guides, how-to pages, help | docs/manual |
| Questions and answers | docs/faq |
| Glossaries | docs/glossary |
| Papers and abstracts | academic/paper |
| Novels and stories | literature/fiction |
| Literary essays | literature/essay |
| Poems and verse | literature/poetry |
| Plays and scripts | literature/play |
| Speeches written to be read aloud | speech/address |
| Records of what was said (press conferences, debates) | speech/transcript |
Payslips, bills, statements and other paperwork have no genre of their own: use business/report, or business/proposal for a quote (Payslips, bills and statements).
What each genre leaves out, and what it adds, is on the Genres page.
With the genre set, the report opens with the genre’s guide: what a good document of that kind does, as questions to check the draft against.
--no-guide leaves it out, and guide: in chaff.yaml changes it (Changing the genre’s guide).
With no genre set, a document is checked as a technical article (blog/tech).
When it looks like another kind, the screen says so under the first line.
The genre it is checked with does not change.
contract.md blog/tech · English genre from the default
Looks like: Contract and terms. Try --genre legal/contract
Run it on your own writing
No settings file, no API key and no language setting are needed. Give it a file and it reads it.
npx chaffjs article.md check this file
npx chaffjs . check every Markdown file here
npx chaffjs docs/ README.md mix directories, files and globs
node_modules, dist, build and coverage are skipped.
If nothing is found to check, the run fails.
That way a run that checked nothing never looks like a pass.
Reading the screen
With no findings, it looks like this.
$ npx chaffjs article.md
article.md blog/tech · English genre from the default
────────────────────────────────────────────────────────────
No findings All judged by machine
(the same text gives the same result every time)
The text was not changed. Fixing it is the writer's job.
70 rules did not run:
abstract-length (the blog/tech genre does not check it)
adverb-without-negation (not a rule for en)
adversative-ga-repeat (not a rule for en)
agentless-passive (the blog/tech genre does not check it)
attachment-not-attached (the blog/tech genre does not check it)
baito-keigo (not a rule for en)
bold-label-list (not a rule for en)
bracket-width-consistency (not a rule for en)
citation-reference-mismatch (the blog/tech genre does not check it)
citation-style-mix (the blog/tech genre does not check it)
clause-chain (not a rule for en)
colloquial-opener (not a rule for en)
colon-lead-in (not a rule for en)
concrete-evidence-density (the blog/tech genre does not check it)
connective-hierarchy (not a rule for en)
cross-doc-broken-link (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cross-doc-duplicate-definition (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cross-doc-fact-conflict (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cross-doc-term-variant (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cushion-phrase-density (the blog/tech genre does not check it)
defined-name-repeated (the blog/tech genre does not check it)
defined-term-form (the blog/tech genre does not check it)
double-keigo (not a rule for en)
email-greeting-closing (the blog/tech genre does not check it)
email-subject-length (the blog/tech genre does not check it)
empty-conclusion (it reads meaning; npx chaffjs test runs it)
exclamation-width-consistency (not a rule for en)
figure-reference-order (the blog/tech genre does not check it)
fullwidth-alnum-consistency (not a rule for en)
hankaku-kana (not a rule for en)
hiragana-fukushi (not a rule for en)
humble-for-others (not a rule for en)
idiom-misuse (not a rule for en)
ime-residue (not a rule for en)
kana-function-word (not a rule for en)
kanji-compound-slip (not a rule for en)
katakana-density (not a rule for en)
katakana-long-vowel (not a rule for en)
kutoten-consistency (not a rule for en)
latin-spacing (not a rule for en)
max-kanji-continuous (not a rule for en)
max-ten (not a rule for en)
misconversion (not a rule for en)
missing-final-period (not a rule for en)
no-doubled-joshi (not a rule for en)
no-mixed-desumasu (not a rule for en)
no-nakaguro-parallel (not a rule for en)
obligation-without-deadline (the blog/tech genre does not check it)
party-order (not a rule for en)
party-role-name (the blog/tech genre does not check it)
preamble-length (the blog/tech genre does not check it)
quote-unnamed-speaker (the blog/tech genre does not check it)
ra-nuki (not a rule for en)
repeated-sentence-ending (not a rule for en)
request-without-deadline (the blog/tech genre does not check it)
requirement-smell (the blog/tech genre does not check it)
risk-disclosure (the blog/tech genre does not check it)
sasete-itadaku (not a rule for en)
stray-space (not a rule for en)
taigen-dome-in-prose (not a rule for en)
tari-unpaired (not a rule for en)
teki-overuse (not a rule for en)
toki-as-condition (not a rule for en)
topic-predicate-distance (not a rule for en)
translationese-density (not a rule for en)
undefined-acronym (the blog/tech genre does not check it)
undefined-term (the blog/tech genre does not check it)
unsourced-number (the blog/tech genre does not check it)
vague-deadline (the blog/tech genre does not check it)
yori-as-from (not a rule for en)
AI-likeness: not scored (too short: 66 words; scored from 200)
With findings, each one gets its own block. This is a run on a short article written for this guide.
$ npx chaffjs sample.md
sample.md blog/tech · English genre from the default
─── line 3 ───────────────────────────────────────────────────
Our end-to-end suite spent most of its time building the same fixtures again on every run, so we started keeping them in…
· Sentence too long
This sentence runs 41 words (limit 40)
In a long sentence the reader loses the subject before reaching the verb.
→ Split it in two at the conjunction.
Relax this rule: npx chaffjs relax max-sentence-length
─── line 11 ──────────────────────────────────────────────────
The restoreFixtures step restores the fixtures.
· Heading echoed
The first sentence repeats the heading "restoreFixtures"
When the first sentence repeats the heading, the reader gains nothing by reading on. A sentence that states a figure the heading does not have (an amount, a number, a date) gives the content even when it repeats the heading, and is not reported.
→ Start from what the heading promised, not from the heading itself.
Relax this rule: npx chaffjs relax heading-echo
────────────────────────────────────────────────────────────
2 notes All judged by machine
(the same text gives the same result every time)
The text was not changed. Fixing it is the writer's job.
70 rules did not run:
abstract-length (the blog/tech genre does not check it)
adverb-without-negation (not a rule for en)
adversative-ga-repeat (not a rule for en)
agentless-passive (the blog/tech genre does not check it)
attachment-not-attached (the blog/tech genre does not check it)
baito-keigo (not a rule for en)
bold-label-list (not a rule for en)
bracket-width-consistency (not a rule for en)
citation-reference-mismatch (the blog/tech genre does not check it)
citation-style-mix (the blog/tech genre does not check it)
clause-chain (not a rule for en)
colloquial-opener (not a rule for en)
colon-lead-in (not a rule for en)
concrete-evidence-density (the blog/tech genre does not check it)
connective-hierarchy (not a rule for en)
cross-doc-broken-link (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cross-doc-duplicate-definition (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cross-doc-fact-conflict (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cross-doc-term-variant (it compares documents, and there is only one to compare (it runs when chaff is given two or more files, or a folder))
cushion-phrase-density (the blog/tech genre does not check it)
defined-name-repeated (the blog/tech genre does not check it)
defined-term-form (the blog/tech genre does not check it)
double-keigo (not a rule for en)
email-greeting-closing (the blog/tech genre does not check it)
email-subject-length (the blog/tech genre does not check it)
empty-conclusion (it reads meaning; npx chaffjs test runs it)
exclamation-width-consistency (not a rule for en)
figure-reference-order (the blog/tech genre does not check it)
fullwidth-alnum-consistency (not a rule for en)
hankaku-kana (not a rule for en)
hiragana-fukushi (not a rule for en)
humble-for-others (not a rule for en)
idiom-misuse (not a rule for en)
ime-residue (not a rule for en)
kana-function-word (not a rule for en)
kanji-compound-slip (not a rule for en)
katakana-density (not a rule for en)
katakana-long-vowel (not a rule for en)
kutoten-consistency (not a rule for en)
latin-spacing (not a rule for en)
max-kanji-continuous (not a rule for en)
max-ten (not a rule for en)
misconversion (not a rule for en)
missing-final-period (not a rule for en)
no-doubled-joshi (not a rule for en)
no-mixed-desumasu (not a rule for en)
no-nakaguro-parallel (not a rule for en)
obligation-without-deadline (the blog/tech genre does not check it)
party-order (not a rule for en)
party-role-name (the blog/tech genre does not check it)
preamble-length (the blog/tech genre does not check it)
quote-unnamed-speaker (the blog/tech genre does not check it)
ra-nuki (not a rule for en)
repeated-sentence-ending (not a rule for en)
request-without-deadline (the blog/tech genre does not check it)
requirement-smell (the blog/tech genre does not check it)
risk-disclosure (the blog/tech genre does not check it)
sasete-itadaku (not a rule for en)
stray-space (not a rule for en)
taigen-dome-in-prose (not a rule for en)
tari-unpaired (not a rule for en)
teki-overuse (not a rule for en)
toki-as-condition (not a rule for en)
topic-predicate-distance (not a rule for en)
translationese-density (not a rule for en)
undefined-acronym (the blog/tech genre does not check it)
undefined-term (the blog/tech genre does not check it)
unsourced-number (the blog/tech genre does not check it)
vague-deadline (the blog/tech genre does not check it)
yori-as-from (not a rule for en)
AI-likeness: not scored (too short: 111 words; scored from 200)
From top to bottom, the screen says:
| Part of the screen | What it means |
|---|---|
| First line | The file, its genre, its language, and where the genre came from |
─── line 3 ─── | One finding starts here; the number is the line |
| The indented sentence | The sentence the finding is about |
· and ⚠ | How serious the finding is (· a note, ⚠ a warning), followed by its title |
| The two lines under the title | What is happening, and why it is hard to read |
The line starting with → | How to fix it |
| Relax this rule | The command that adjusts that rule for your team |
| The count at the end | How many findings, and that all of them were judged by machine |
| Rules that did not run | The rules not used this time, each with the reason |
The same text gives the same result every time. Rules that did not run are listed with their reasons, so you can see what was checked and what was not.
Three ways to respond to a finding
Any of them is fine.
| Way | When | What to do |
|---|---|---|
| Fix it | The finding is right | Rewrite the text |
| Silence this spot | The finding is right, but here it is on purpose | Write <!-- stet: rule-id — reason --> |
| Change the rule | It does not fit how your team writes | Type npx chaffjs relax rule-id --why "reason" |
The third one matters. Without a way to change the rule, “too noisy, stop using it” is where it ends.
Choosing what to silence
stet marks a finding as intended. It is written as a Markdown comment, so readers never see it.
There are three reaches to choose from.
| Written as | Silences |
|---|---|
<!-- stet: bold-density — a glossary, on purpose --> | The paragraph just below it (or the one heading, list or table) |
<!-- stet-section: bold-density — a list --> | Up to the next heading |
<!-- stet-file: ai-tell, rule-of-three — mostly quotations --> | The whole file |
Write the reason after —, so whoever reads it later can see why it was silenced.
If you keep silencing the same rule, it is time to change the rule instead.
What to read next
- What chaff does for internal rules or a business report, with real examples, is in What chaff does for each kind of document.
- Everything chaff can find, with an example and its real output, is listed in the Reference.
- To fit rule strength and genre to your team, read Configuration.
- Every command and option is listed in Commands.