Business reports
A business report says what happened, and who does what next.
chaff finds, by machine, the writing habits that get in the way of that.
The genre is business/report. White papers and internal documents are checked under the same genre.
What usually goes wrong
A report blurs most when its writer would rather not commit.
| Habit | Example |
|---|---|
| An opening that fits any document | “In today’s fast-paced world, the enquiries we receive…” |
| Stacked hedges | “it could perhaps be argued that our support team may need to…” |
| A passive that never says who acts | “Updating the FAQ page is being considered.” |
| A summary that only repeats | “As described above, … We will continue to strive for improvement.” |
The reader cannot tell what was decided, or whom to ask.
What chaff checks, and what it does not
These are the main rules for this genre.
| Rule | What it finds | When it runs |
|---|---|---|
padded-intro | An opening that fits any document | Always |
max-sentence-length | A sentence that runs too long | Always (info) |
excessive-hedging | Hedges stacked together | Always |
agentless-passive | A passive that never says who did it | When chaff.yaml turns it on |
empty-conclusion | A conclusion with nothing in it | When an AI reads it, with npx chaffjs test |
agentless-passive reports on most business documents people write, so it is off for this genre until you turn it on (below).
info marks a place to reread; it never fails a run.
Some things are left out on purpose.
- chaff never rewrites the text. Fixing is the writer’s job.
- It does not check that a number is true. Whether there really were 412 enquiries is for the writer to confirm.
- Whether a conclusion says anything is not decided by machine alone. It needs the meaning, so it goes to an AI.
Try it
If you have not set up yet, do Getting ready first.
The report below was saved as enquiries.md.
# Report on customer enquiries in September
## Background
In today's fast-paced world, the enquiries we receive from customers are becoming more and more varied, and it could perhaps be argued that our support team may need to keep up with that change.
## September
We received 412 enquiries in September, up from 356 in August. **In particular**, enquiries about billing rose **sharply**; most came from customers who had moved to the new price plan and could not read their invoice, so many of them took a long time to answer and for several of them we needed help from the billing team before anyone could reply.
The first reply took 6.2 hours on average, over our target of 4 hours.
## What we are doing
Updating the FAQ page for billing enquiries is being considered. Adding staff is also being discussed.
## Summary
As described above, enquiries rose in September and the first reply took longer than our target. We will continue to strive for improvement going forward.
In the folder that holds the document, type:
npx chaffjs enquiries.md --genre business/report check enquiries.md as a report
$ npx chaffjs enquiries.md --genre business/report
Guide: Report (business/report)
Before the findings below, check the draft against these.
- Does the opening give the finding (what was learned, or what happened)?
- Do the numbers have a source and a date, and do totals and percentages add up?
- Are facts kept apart from opinions and guesses?
- Are the next actions given with an owner and a deadline?
Rules that matter most for this genre: concrete-evidence-density, cushion-phrase-density, empty-conclusion, padded-intro, preamble-length, quote-unnamed-speaker, request-without-deadline, risk-disclosure, unsourced-number
Change it with guide: in chaff.yaml; leave it out with --no-guide.
════════════════════════════════════════════════════════════
enquiries.md business/report · English genre from --genre
─── line 5 ───────────────────────────────────────────────────
In today's fast-paced world, the enquiries we receive from customers are becoming more and more varied, and it could per…
⚠ Padded opening
"in today's fast-paced world" is an opening that fits any article
An opening that fits any article gives the reader no reason to continue.
→ Start from a concrete situation, or from the claim only this piece makes.
Relax this rule: npx chaffjs relax padded-intro
─── line 5 ───────────────────────────────────────────────────
In today's fast-paced world, the enquiries we receive from customers are becoming more and more varied, and it could per…
⚠ Too much hedging
This sentence stacks 3 hedges ("could, perhaps, may")
The more the text avoids claiming, the less the reader can tell what was decided. One hedge is caution; many mean nobody is accountable. chaff looks two ways. A sentence that stacks hedges ("may possibly", "it could perhaps be argued") is reported on its own, even in a short document. Too many hedges across the whole document is measured only on longer documents. When counting hedges stacked in a sentence, a hedge inside a quotation is the speaker's, not the writer's, and is left out.
→ Commit where you can. Where you cannot, say what you would need to know. Where hedges are stacked, keep one or drop them all and commit.
Relax this rule: npx chaffjs relax excessive-hedging
─── line 9 ───────────────────────────────────────────────────
In particular , enquiries about billing rose sharply ; most came from customers who had moved to the new price plan and …
· Sentence too long
This sentence runs 53 words (limit 45)
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
────────────────────────────────────────────────────────────
2 warnings, 1 note 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.
85 rules did not run:
abstract-length (the business/report genre does not check it)
adverb-without-negation (not a rule for en)
adversative-ga-repeat (not a rule for en)
agentless-passive (the business/report genre does not check it)
ai-structure (the business/report genre does not check it)
attachment-not-attached (the business/report 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 business/report genre does not check it)
citation-style-mix (the business/report genre does not check it)
clause-chain (not a rule for en)
closing-cliche (the business/report genre does not check it)
code-fence-language-mix (the business/report genre does not check it)
colloquial-opener (not a rule for en)
colon-lead-in (not a rule for en)
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))
default-value-mismatch (the business/report genre does not check it)
defined-name-repeated (the business/report genre does not check it)
defined-term-form (the business/report genre does not check it)
deprecated-option-used (the business/report genre does not check it)
double-keigo (not a rule for en)
email-greeting-closing (the business/report genre does not check it)
email-subject-length (the business/report 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 business/report genre does not check it)
fullwidth-alnum-consistency (not a rule for en)
hankaku-kana (not a rule for en)
heading-command-missing (the business/report genre does not check it)
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 business/report genre does not check it)
paragraph-length-variance (the business/report genre does not check it)
parameter-table-mismatch (the business/report genre does not check it)
party-order (not a rule for en)
party-role-name (the business/report genre does not check it)
prerequisite-after-step (the business/report genre does not check it)
quote-without-source (the business/report genre does not check it)
ra-nuki (not a rule for en)
repeated-sentence-ending (not a rule for en)
requirement-smell (the business/report genre does not check it)
return-type-mismatch (the business/report genre does not check it)
risk-disclosure (it reads meaning; npx chaffjs test runs it)
rule-of-three (the business/report genre does not check it)
sasete-itadaku (not a rule for en)
section-length-uniformity (the business/report genre does not check it)
sentence-rhythm (the business/report genre does not check it)
shell-prompt-mix (the business/report genre does not check it)
step-statement-mix (the business/report genre does not check it)
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)
ui-label-variant (the business/report genre does not check it)
undefined-function-call (the business/report genre does not check it)
undefined-term (the business/report genre does not check it)
unsourced-number (it reads meaning; npx chaffjs test runs it)
vague-deadline (the business/report genre does not check it)
version-mismatch (the business/report genre does not check it)
version-order (the business/report genre does not check it)
warning-without-action (the business/report genre does not check it)
yori-as-from (not a rule for en)
AI-likeness: not scored (too short: 153 words; scored from 200)
There are three findings. Under each line number, chaff shows the start of the sentence it is about.
| Line | Finding | What it means | How to fix the text |
|---|---|---|---|
| 5 | Padded opening | “In today’s fast-paced world” could open any report | Start from what happened in September |
| 5 | Too much hedging | “could”, “perhaps” and “may” in one sentence | Say it outright. If you cannot, say what you would need to know first |
| 9 | Sentence too long | 53 words, over this genre’s limit | Split at the semicolon and at “so” |
The guide at the top
The screen above opens with the genre’s guide: what a good report does, as questions to check the draft against, and the rules that matter most for this genre.
chaff prints it because the genre was set with --genre.
Most of the questions are ones no rule can answer, so read them before the findings.
An AI that rewrites the text works to the same lines (Using the guide with an AI).
A guessed or default genre prints no guide, and --compact and --no-guide leave it out.
A team changes the lines with guide: in chaff.yaml (Changing the genre’s guide).
Reading the “did not run” list
At the end, the screen lists the rules it did not use this time, each with its reason. That way “no findings” is never mistaken for “checked everything and found nothing”. There are three reasons.
| Reason | What it means |
|---|---|
| the genre does not check it | The genre leaves the rule out, by a written reason or because the rule reports on most documents of its kind. rules: in chaff.yaml turns it on |
| not a rule for en | The rule is for Japanese documents only |
| it reads meaning; npx chaffjs test runs it | A machine alone cannot decide it. It runs when an AI reads the passage, with npx chaffjs test |
Turning on the passive check
agentless-passive is off for business documents, because most reports people write use the passive somewhere.
A team that wants every report to say who acts turns it on in chaff.yaml:
rules:
agentless-passive: normal
Here --compact prints each finding on two lines: the line and column, then the rule’s name.
$ npx chaffjs enquiries.md --genre business/report --compact
enquiries.md business/report · English genre from --genre
5:1 warning "in today's fast-paced world" is an opening that fits any article
padded-intro
5:112 warning This sentence stacks 3 hedges ("could, perhaps, may")
excessive-hedging
9:66 info This sentence runs 53 words (limit 45)
max-sentence-length
3 findings, 85 rules not run
The three new findings, in plain words:
| Line | Finding | What it means | How to fix the text |
|---|---|---|---|
| 5 | Passive with no actor | “it could be argued”: argued by whom? | Say who thinks so, or just say it |
| 15 | Passive with no actor | “is being considered”: by whom? | “The support team will add …” |
| 15 | Passive with no actor | “is being discussed”: by whom? | “The head of support will decide …” |
Checks that read meaning go to an AI
The “Summary” section only repeats what came before.
That takes reading the meaning to decide, so chaff hands it to an AI with npx chaffjs test.
With --dry-run, you can see what would be sent without sending anything.
npx chaffjs test enquiries.md --genre business/report --dry-run show what would go to the AI (sends nothing)
The first part of the screen is the same machine check as above. After it comes:
enquiries.md sends 2 passages out of 8 sentences (the API was not called)
1 to send Empty conclusion (narrowed by machine)
1 to send No risk disclosed (narrowed by machine)
0 to send Number without a source (narrowed by machine)
The text sent for the first one:
As described above, enquiries rose in September and the first reply took longer than our target. We will continue to strive for improvement going forward.
────────────────────────────────────────────────────────────
2 passages in all would be sent. With --dry-run the API was not called.
Sent to: anthropic / claude-opus-5 (no credentials)
Only the two passages the machine narrowed down are sent, never the whole document.
To have an AI actually read them, you need an API key from the AI company.
Anthropic is the default, and ai_backend in chaff.yaml switches to OpenAI.
Where to put the key is in the README.
Without an AI, every machine check still runs.
Not typing the genre every time
Without --genre, chaff reads the document as a technical article (blog/tech).
A report is hard to tell from other kinds by its look, so no line suggests a genre for it.
Set the genre once for the folder where your reports live:
npx chaffjs init --genre business/report create chaff.yaml here, with the genre in it
After saying it created chaff.yaml (the settings file) and .gitignore, it shows:
The genre is business/report. If that is wrong, change genre in chaff.yaml.
List: npx chaffjs genres
Next:
npx chaffjs . check every Markdown file here
From then on, npx chaffjs enquiries.md in this folder checks the file as a report.
Silencing one spot on purpose
Sometimes nobody has yet decided which team will update the page. Then put this line just above the paragraph to silence the passive findings there:
<!-- stet: agentless-passive — which team updates the page is not decided yet -->
Updating the FAQ page for billing enquiries is being considered. Adding staff is also being discussed.
stet marks a spot whose finding is silenced. Write the reason after —.
It is a Markdown comment, so nobody opening the document sees it.
Run it again and the two passive findings on line 15 are gone, and the first line counts what was silenced.
$ npx chaffjs enquiries.md --genre business/report --compact
enquiries.md business/report · English genre from --genre
5:1 warning "in today's fast-paced world" is an opening that fits any article
padded-intro
5:112 warning This sentence stacks 3 hedges ("could, perhaps, may")
excessive-hedging
2 findings, 85 rules not run
Changing a rule for the whole team
When it is not one spot but how your team works, relax the rule instead. Say your team’s reports always open the Background section with a set phrase:
npx chaffjs relax padded-intro --why "our Background sections open with a set phrase"
This adds the rule to chaff.yaml, with the reason, the date and who typed it:
# Padded opening
# An opening that fits any article gives the reader no reason to continue.
padded-intro: relaxed # 2026-09-30 our Background sections open with a set phrase / isamu
Run it again and the “In today’s fast-paced world” opening is no longer flagged. The reason stays in the file, so whoever comes later can see why it was relaxed.
After fixing
The report now opens with what happened, drops the hedges, and says who does what and when. The summary became what the next report will show.
# Report on customer enquiries in September
## In short
Enquiries rose in September, and the first reply took longer than our 4-hour target. Most of the rise was customers asking how to read the invoice for the new price plan.
## September
We received 412 enquiries in September, up from 356 in August. The rise was in billing enquiries. Most came from customers who had moved to the new price plan and could not read their invoice.
The first reply took 6.2 hours on average, over our target of 4 hours.
## What we are doing
The support team will add a guide to reading the invoice to the FAQ page during October. The head of support will decide in November whether to add staff, once the October figures are in.
## What the next report will show
Whether the first reply in October was back within the 4-hour target.
$ npx chaffjs enquiries-fixed.md --genre business/report
Guide: Report (business/report)
Before the findings below, check the draft against these.
- Does the opening give the finding (what was learned, or what happened)?
- Do the numbers have a source and a date, and do totals and percentages add up?
- Are facts kept apart from opinions and guesses?
- Are the next actions given with an owner and a deadline?
Rules that matter most for this genre: concrete-evidence-density, cushion-phrase-density, empty-conclusion, padded-intro, preamble-length, quote-unnamed-speaker, request-without-deadline, risk-disclosure, unsourced-number
Change it with guide: in chaff.yaml; leave it out with --no-guide.
════════════════════════════════════════════════════════════
enquiries-fixed.md business/report · English genre from --genre
────────────────────────────────────────────────────────────
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.
85 rules did not run:
abstract-length (the business/report genre does not check it)
adverb-without-negation (not a rule for en)
adversative-ga-repeat (not a rule for en)
agentless-passive (the business/report genre does not check it)
ai-structure (the business/report genre does not check it)
attachment-not-attached (the business/report 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 business/report genre does not check it)
citation-style-mix (the business/report genre does not check it)
clause-chain (not a rule for en)
closing-cliche (the business/report genre does not check it)
code-fence-language-mix (the business/report genre does not check it)
colloquial-opener (not a rule for en)
colon-lead-in (not a rule for en)
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))
default-value-mismatch (the business/report genre does not check it)
defined-name-repeated (the business/report genre does not check it)
defined-term-form (the business/report genre does not check it)
deprecated-option-used (the business/report genre does not check it)
double-keigo (not a rule for en)
email-greeting-closing (the business/report genre does not check it)
email-subject-length (the business/report 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 business/report genre does not check it)
fullwidth-alnum-consistency (not a rule for en)
hankaku-kana (not a rule for en)
heading-command-missing (the business/report genre does not check it)
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 business/report genre does not check it)
paragraph-length-variance (the business/report genre does not check it)
parameter-table-mismatch (the business/report genre does not check it)
party-order (not a rule for en)
party-role-name (the business/report genre does not check it)
prerequisite-after-step (the business/report genre does not check it)
quote-without-source (the business/report genre does not check it)
ra-nuki (not a rule for en)
repeated-sentence-ending (not a rule for en)
requirement-smell (the business/report genre does not check it)
return-type-mismatch (the business/report genre does not check it)
risk-disclosure (it reads meaning; npx chaffjs test runs it)
rule-of-three (the business/report genre does not check it)
sasete-itadaku (not a rule for en)
section-length-uniformity (the business/report genre does not check it)
sentence-rhythm (the business/report genre does not check it)
shell-prompt-mix (the business/report genre does not check it)
step-statement-mix (the business/report genre does not check it)
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)
ui-label-variant (the business/report genre does not check it)
undefined-function-call (the business/report genre does not check it)
undefined-term (the business/report genre does not check it)
unsourced-number (it reads meaning; npx chaffjs test runs it)
vague-deadline (the business/report genre does not check it)
version-mismatch (the business/report genre does not check it)
version-order (the business/report genre does not check it)
warning-without-action (the business/report genre does not check it)
yori-as-from (not a rule for en)
AI-likeness: not scored (too short: 127 words; scored from 200)
No findings. The three checks that read meaning run only when an AI reads the passages.
What to read next
- How to read the screen, and the three ways to respond to a finding, are in Getting started.
- To fit rule strength to your team, read Configuration.
- Every rule is explained in the rule reference.
- For another kind of document, go back to What chaff does for each kind of document.