appeal-score — make the prose read human, and prove it

SkillAI & models

One repeatable pass that makes a doc read like a person wrote it, not a model. Runs the doc-appeal scorecard (tools/doc_appeal_scorecard.py), turns each HARD defect into a required edit (em-dash flood, bold-emphasis flood, run-on / overlong sentences, walls of text, stacked "X, not Y" contrast frames, a dense or unanchored lead, LLM-scaffolding phrases) and each SOFT signal into a judgment call, retires appeal-debt worst-axis-first WITHOUT changing any claim, number, or link, re-measures to PROVE the debt dropped, and commits only. Use when this named workflow matches the task.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the appeal-score — make the prose read human, and prove it skill

What this skill tells your AI

The instructions your AI receives, as published by anthony-chaudhary/fak in .agents/skills/appeal-score/SKILL.md and read by ahel’s review.

What this does. Readers (and answer engines) bounce off prose that reads like a machine wrote it: an em-dash in every sentence, run-ons that never breathe, walls of text, the same "X, not Y" contrast frame over and over, formulaic throat-clearing ("here's the thing", "at its core"). The freshness auditor checks whether a doc is correct; this pass checks whether it lands: whether a cold reader gets it, trusts it, and can try it without slogging through machine prose. It makes "read more human" a repeatable, provable pass instead of a one-time vibe edit that decays.

The shape: run the scorecard → fix every HARD defect → weigh every SOFT signal → re-measure to prove appeal-debt dropped → commit only the doc lane.

The headline number is appeal-debt: the count of concrete, re-derivable prose defects. Drive it toward zero and "more human" becomes a number you moved, not a claim you made.


The one rule that overrides everything: never change the claim

This is a voice pass, not a content edit. The repo keeps an honesty ledger, so the prose may change but the meaning may not:

  • Every number, claim, and capability statement stays identical. Splitting a sentence or cutting an em-dash must not drop, weaken, soften, or invent a figure. 9.7× stays 9.7×; a [SIMULATED] caveat stays; an honest fence ("self-host only", "design targets, not measurements") stays.
  • Every link target stays byte-identical. Verify before and after: grep -oE '\]\([^)]+\)' DOC | sort -u must match. You are rewording prose, not re-pointing links.
  • Simplify the words, never the claim (the Feynman law, shared with refresh-readme). Lead with the plain idea, name the term in parens on first use. Accuracy is non-negotiable.

If a fix would require changing a claim, stop. That defect is out of scope for this pass.


What counts as an LLM tell (the HARD defects this pass retires)

Each is one unit of appeal-debt, deterministic and re-derivable. Retire them worst-axis-first:

TellAxisThe human fix
Em-dash flood (past ~1 per 285 words)voiceReplace most with a period, comma, colon, or parentheses. Keep a few where the pause truly earns it.
Overlong sentence (≥32 words) / run-on (≥4 commas)claritySplit into two. Pair list items with "and" to cut commas; turn a 5-item enumeration into a real bulleted list.
Wall of text (a prose paragraph >80 words)scannabilityBreak into 2–3 paragraphs, or lift the structure into a list/table. (A genuine multi-bullet list is exempt — it's already scannable.)
Bold-emphasis flood (past ~1 bold span per 100 words)voiceCut to the few emphases that truly guide the eye. A phrase bolded in nearly every line stops guiding anything and reads generated. (Capped, so one flood can't own the axis.)
Stacked contrast frame ("X, not Y" / "not X, it's Y" past a budget of 1)voiceVary the shape: "rather than", "but not", or just rewrite. One is punchy; a habit is a tic.
Dense lead (an essay of prose before the first section break)priorityFront-load the value; push the framing down. Lift a "what it is / try it" line up so a skimmer reaches the path to value fast.
Unanchored lead (no H2 section anchor near the top)organizationAdd an early heading or lift a section up so a skimmer's outline isn't blank until deep in the page.
LLM-scaffolding phrase ("here's the thing", "at its core", "it's important to note", "when it comes to")voiceCut it, or say the thing plainly. A real Feynman move ("think of it as…", a concrete example) is good and is NOT a tell — keep those.
Cliché / marketing AI-tell ("leverage", "seamless", "robust", "best-in-class")voiceUse the plain word.

SOFT signals (passive-voice density, low sentence-length variety, an emphasis-flood overflow past the cap, hedging, inconsistent name casing) lower the score but are never appeal-debt; they are writing judgment, not mechanical fact. Weigh them rather than grinding on them.

v2 calibration (2026-06-23). The thresholds above were tightened after the front door saturated v1 (95/100, four trivial defects): a front page is skimmed, so its sentence/paragraph budgets are stricter than a body doc's, the bold flood was promoted SOFT→HARD, and a dense or unanchored lead is now HARD. The README lands in the low 50s on v2 — real headroom again — and an appeal pass drives it back toward the 90s, worst-axis-first.


Step 1 — Run the scorecard (it builds your work-list)

From the repo root:

python tools/doc_appeal_scorecard.py                       # human scorecard for README.md
python tools/doc_appeal_scorecard.py --target docs/FAQ.md  # any other doc
python tools/doc_appeal_scorecard.py --json                # machine payload (the loop uses this)

It scores five axes (clarity · priority · voice · scannability · organization) into an appeal score (0–100, A–F) and an appeal-debt integer, and prints the work-list: every HARD defect with its line and a snippet, then the SOFT signals. It is read-only and never edits the doc.

Step 2 — Retire appeal-debt worst-axis-first

Take the weakest axis first (the scorecard names it). For each HARD defect, apply the human fix from the table above. After a batch, re-run the scorecard and watch the number fall; that loop (fix, re-measure, fix again) is the whole method. A buried lead or a missing TL;DR (the priority axis) is worth a one-line summary block near the top that a skimmer or an answer engine can lift.

Step 3 — Weigh the SOFT signals, then stop

Read the SOFT list once. Fix the cheap, real ones (a stray cliché, a monotone run of same-length sentences). Do not chase SOFT signals to zero. Over-correction is its own tell. Prose chopped into robotic staccato to dodge a metric reads worse than the original; a human writer varies sentence length and keeps the occasional long sentence. Aim for "a person wrote this", not "the linter is happy".

Step 4 — Re-measure and confirm the drop

Re-run the scorecard. State the before/after (e.g. "appeal-debt 70 → 2, voice 24 → 97"). Re-confirm links are byte-identical and no number moved. For the README specifically, also re-run tools/readme_freshness_audit.py so a voice edit didn't trip a freshness FAIL (a dead link, a stale version pin), and re-stamp the appeal-verified marker near the top with today's date and the new score.

Step 5 — Commit only the doc lane, by explicit path

This is a shared trunk; commit your doc, never a peer's work:

fak sync reconcile --apply
fak commit --path README.md -F <msgfile> [--push]
fak sync push
  • Stage by explicit path via fak commit --path, never git add -A.
  • Doc-only diff → a docs(scope): <verb> … subject (lead with a recognized verb: rewrite/cut/split/trim), end with the (fak <leaf>) trailer. A code-effect prefix on a docs-only diff overclaims.
  • On Windows, pass the message via a file (-F), not a here-string.
  • If a peer's MERGE_HEAD is set, wait for it to clear; don't abort or work around it. Then commit by explicit path.
  • Stay on the trunk (main); push promptly via fak sync push (or --push).

Scope: what this pass touches, and what it must not

  • In scope: reader-facing prose docs, README.md first (the front door), then the next-most-important by importance × debt (docs/FAQ.md, GETTING-STARTED.md, docs/fak/tutorial.md, docs/concepts-and-story.md, the docs/explainers/*).
  • Out of scope: structured ledgers and generated docs. In a file like CLAIMS.md the in - [TAG] claim — detail lines is a format separator, not a voice tell; "fixing" it would corrupt the ledger (and trip claims-lint). The scorecard over-counts em-dashes in such list-structured docs, so judge by eye and skip them. For anything machine-generated, fix the generator rather than its output.

Verified reads (closure evidence)

Record each burndown child's before/after here so the next worker re-measures against the same baseline instead of re-deriving it.

  • README.md — #1421 (B5 of the #1414 portfolio burndown). Baseline read quoted in the issue body: 71.5/100 (C), 28 units of appeal-debt, weakest on clarity 54 and priority 59. Verified read on trunk 2026-07-03: 100.0/100 (A), zero appeal-debt, all five axes at 100 (clarity 100, priority 100, voice 100, scannability 100, organization 100). The lift landed through intervening front-page work (the README refresh commits), so the baseline numbers were already stale by the time B5 was picked up — measure before editing, don't "fix" passages the tool no longer flags. Re-derive with one command from the repo root:

    python tools/doc_appeal_scorecard.py --json   # README.md 100.0/100 (A), appeal_debt 0
    

    The A is not frozen: README is a hot shared-trunk file, so re-run the scorecard at closure time rather than trusting this snapshot.

When to run this

  • After a release, a headline-number change, or any substantial README edit.
  • When a doc reads dense or "written by a model" and you want it provably human.
  • On a /loop cadence over the core prose docs to keep them from drifting back toward machine voice.

The scorecard is the README's voice checking-layer, the way refresh-readme is its freshness layer and quality-score is the code's. Same discipline: a number you can move, and prove you moved.

Signals

GitHub stars
38
Forks
15
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
appeal-score
Source
github.com/anthony-chaudhary/fak