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.

HabitExample
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.

RuleWhat it findsWhen it runs
padded-introAn opening that fits any documentAlways
max-sentence-lengthA sentence that runs too longAlways (info)
excessive-hedgingHedges stacked togetherAlways
agentless-passiveA passive that never says who did itWhen chaff.yaml turns it on
empty-conclusionA conclusion with nothing in itWhen 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.

LineFindingWhat it meansHow to fix the text
5Padded opening“In today’s fast-paced world” could open any reportStart from what happened in September
5Too much hedging“could”, “perhaps” and “may” in one sentenceSay it outright. If you cannot, say what you would need to know first
9Sentence too long53 words, over this genre’s limitSplit 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.

ReasonWhat it means
the genre does not check itThe 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 enThe rule is for Japanese documents only
it reads meaning; npx chaffjs test runs itA 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:

LineFindingWhat it meansHow to fix the text
5Passive with no actor“it could be argued”: argued by whom?Say who thinks so, or just say it
15Passive with no actor“is being considered”: by whom?“The support team will add …”
15Passive 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.