prosemeter

Deterministic scoring for prose.

Agents close the loop on code with compilers, linters, and tests. Prose has no such signal.

prosemeter is one. Give it a document and a named profile. It returns a 0–100 score, 15 per-dimension scores, and findings with line numbers and fix hints — enough for an agent to revise, measure, and know when to stop.

The scoring path is deterministic. Same input, same output. No model calls, no network, no randomness. That is what makes it usable as a fitness function.

pnpm add prosemeterScore a document ↓GitHub

The same answer, two registers

Both documents below answer the same question with the same fix. One scores 92 on the chatprofile. The other scores 49. Toggle between them and watch which dimensions move.

A correct answer nobody wants to read. Every idea is right; the register buries all of them.

49/100·threshold 75below·194 words

Dimension scores, worst first
grade-band2median grade 25.8 vs band 7–12 (FK 25.8, Fog 30.5, SMOG 24.1, CL 25.1, ARI 26.0); Flesch Reading Ease -40.2
sentence-simplicity267 hard sentences / 194 words
clarity454 wordy phrase(s) / 194 words
lexical-diversity31MTLD 195.1 vs band 50–120
directness712 weasel/hedge word(s) / 194 words
sentence-variety80sentence-length variation 0.22 vs band 0.4–0.9
5 dimensions at 100
active-voice1000 passive construction(s) / 194 words
concision1000 concision issues (repeats, redundant acronyms, clichés) / 194 words
paragraph-length1005 paragraph(s) vs band 1–6 sentences
terminology-consistency1000 inconsistent term use(s) / 194 words
spelling-consistency1000 mixed-spelling occurrence(s) / 194 words
heading-hierarchydisabled by profile
section-lengthdisabled by profile
document-balancedisabled by profile
acronym-definitiondisabled by profile

Findings

sentence-simplicity

clarity

directness

Try another document

Scored in your browser. Nothing was uploaded, and there is no model in the loop.

A score is not enough

A linter tells an agent what is wrong. It does not tell it whether the rewrite helped, or when to stop rewriting. Those are the two questions that make a revision loop terminate.

prosemeter answers both. compareBaseline diffs a result against the previous one and reports which findings resolved and which are new. checkConvergence reads a score history and returns one of five verdicts. An agent revises while the trend climbs and stops on anything else.

const result  = score(draft, { profile: "readme" })
const delta   = compareBaseline(current, previous)
const verdict = checkConvergence([62, 71, 74, 74.5], { threshold: 80 })
// "improving" | "plateaued" | "oscillating" | "regressing" | "converged"

The distinct stop reasons let a harness attach policy. regressing means revert to the highest-scoring earlier draft rather than push the latest one further.

You cannot buy the score by shortening sentences

Here is a document that games every obvious lever:

The cat sat. The cat is big. The cat is red. It ran. It ran fast.

It scores 100 on sentence-simplicity, 100 on clarity, and 100 on concision. It scores 71 overall and fails the chat profile's threshold of 75.

grade-band is the reason. It is a band, not a goal — 1.0 inside the target range, falling off oneither side. This document reads at grade -2.1 against a floor of 7, so the dimension carrying the profile's largest weight scores 5.

The design generalizes. Targets are ranges, so there is no single number to run away with, and every dimension that could be gamed by chopping sentences is counterweighted by one that punishes it.

Measured, not asserted

32% shorter.

A style instruction against a no-instruction control on the same six tasks: 438 → 298 words, jargon 10.7% → 8.7%, sentence-simplicity 50.2 → 69.9.

n=90 · claude-opus-5 · 2026-08-03

Naming the swap is what works.

“Be concise. Avoid jargon. Write clearly.” reaches 350 words and 10.3% jargon — most of the length win, almost none of the vocabulary win. Telling a model which words to prefer moved jargon; telling it to avoid jargon did not.

Instruction wording does not reach factual accuracy.

Two runs and one targeted mechanism test. No effect. The instruction written specifically to fix an observed error failed on that exact error in 4 of 5 replicates.

Read dimensions, not the composite.

Across seven instruction variants the composite spanned 79.5–86.6 while the spread within a single variant was about 15 points. Over the same data the dimensions moved 2–3x.

236 scored answers across three runs, plus a Sonnet transfer check. The method and the caveats →

One engine, four ways in

Library

ESM, typed, returns an Either.

pnpm add prosemeter

CLI

Exits non-zero below a threshold.

prosemeter score README.md

MCP server

Five tools over stdio.

npx @prosemeter/mcp

Claude Code plugin

Server plus the revise/stop skill.

/plugin install prosemeter@prosemeter

All four, with the details →

What it is not

A deterministic prose metric is a proxy, and any agent optimizing a proxy will find its seams. The bands blunt the worst of that. The honest framing matters more than the guardrail.

prosemeter is a floor and a loop terminator, not a quality oracle. It tells an agent whether a draft cleared the objective bar and when to stop iterating. It does not tell you the prose is good.

It also cannot check facts. Two eval runs and one targeted mechanism test found that style instructions move length and vocabulary by 2–3x and move factual accuracy not at all. A high score is not a correctness signal. Verify separately.

It measures how a document reads, not whether it makes sense to a particular reader — a gap we have measured and not yet closed →

prosemeter 0.3.2. Every score on this page was computed at build time.