# Doc Quality

> Docs-health readability scan — two axes: QUALITY (bloat: filler, repetition, walls of text, buried leads; clarity: unexplained jargon, ambiguity, sentences that fight the reader) and language MECHANICS (typo, grammar, spelling, orthography — including script-level defects like decomposed characters that render right but break search, doubled spaces, wrong ellipsis/quote characters, an em-dash spacing convention check against `emDash` — engine-detected, off by factory default — spacing rules of the doc's language). Catches UNREADABLE or MALFORMED-LANGUAGE docs. Triggers on: "/doc-quality", "doc-quality", "tighten this doc", "proofread", "typos and grammar", "is this readable". Mechanical layer = deterministic mechanics (Unicode normalization, spacing, punctuation shape — ~free, report-only); semantic layer = bloat/clarity judgment + grammar in context (paid, consent-gated). Honors the project's own style/language rules where defined. Severity judged by context, never mechanical.

- Skill: `thecolliery/doc-quality` (Agent Skill)
- Install (CLI): `npx skillmds@latest add thecolliery/doc-quality`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thecolliery/doc-quality/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: TheColliery (https://skillmd.com/u/thecolliery)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/thecolliery/doc-quality

---


# Doc-Quality

Answer in the USER'S language; keep technical terms, commands, paths, and check ids verbatim.

Find what makes a doc hard to read or mechanically malformed. Report CONFIRMED findings; style opinions stay SUSPECTED.

## Parameters
- **SCOPE:** named files (default when given) | touched doc files this session | whole repo docs — `.md`/`.mdx`/`.markdown`/`.rst`/`.txt`/`.adoc`/`.asciidoc`/`.org` (confirm first if > 20 files).
- **TIER:** Quick = mechanical mechanics only (~free) · Full = adds bloat/clarity + grammar-in-context (paid). Default from `quickVsFull` (`.coalledger.json`, global + project merge); Full is always a separate consent.

## The two axes
| axis | layer | catches |
|---|---|---|
| language mechanics | mechanical | decomposed characters that render identically but break search/sort (normalize-and-compare), doubled spaces, mixed or wrong quote/ellipsis/dash characters, an em-dash spacing mismatch against the configured `emDash` convention (engine-detected, see Method step 2b — `off` by factory default, so this row is inert until a project sets it), the doc language's own spacing/orthography rules, obvious misspellings |
| quality | semantic | filler and repetition (the same point twice), walls of text, buried leads, unexplained jargon for the doc's audience, ambiguous instructions |

## Method
1. **House rules first:** if the project defines its own style or language rules (a style guide, formatting conventions, per-language orthography rules), those BIND — enforce them over any general rule, and never fight a documented deliberate choice.
2. **Quick (mechanics):** deterministic checks per the table — a mechanics finding is CONFIRMED only when it is objectively wrong in the doc's language (a decomposed character, a doubled space), not a stylistic preference.
   - **2b. Em-dash convention (`emDash`, `.coalledger.json`, global + project merge):** if `off`, skip this step entirely—never invoke the engine, never report a finding. Otherwise, the engine ships INSIDE this skill folder at `./lib/`, so the skill works even when it travels alone. Your context carries this skill's **base directory**—substitute it for `<skill base dir>` and run exactly:
     `cd "<skill base dir>" && node ./lib/emdash.mjs --mode=<unspaced|spaced> <absolute-file.md> [more absolute paths ...]`
     The `cd` is REQUIRED — `./lib/` resolves against the skill folder, never your project cwd. Target docs must be ABSOLUTE paths, since cwd is now the skill folder. `--mode` is the CONFIGURED `emDash` value verbatim (never `off` — that branch already returned above). Output is `file:line:col: context` per hit, one line per finding; a bare `TOTAL: N` line closes the run. Never re-derive this by eye — the engine already handles fenced/inline code, URLs, Thai lines, blockquotes, and LICENSE/NOTICE/COPYING-class files; see its own header for the exclusion classes and the one class it cannot do (an inline quotation with no blockquote marker — a known, stated over-report the caller adjudicates, never guessed away).
3. **Full (quality):** judge bloat/clarity for the doc's audience and purpose. A cut proposal must not change meaning — quality fixes trim fat, never meat. Grammar/typo judgment runs in the doc's own language.
4. **Word-choice is a MEANING decision, not a typo:** an unusual-but-intentional word, register, or voice gets flagged as SUSPECTED with a question, never "corrected".
5. **Severity by CONTEXT** (never a fixed map), then honor `severityFloor`: an ambiguous instruction readers must execute = HIGH; heavy bloat on a front-door doc = MEDIUM; a typo in prose = LOW, and an em-dash convention mismatch is LOW too — it is a style consistency finding, not a comprehension break (a typo inside a command or identifier is doc-grounding territory — it breaks, it does not just read badly). `scanEverything: true` bypasses the floor this run — report everything down to `low` — and say so: state that `severityFloor` was bypassed, never that every scope cut was bypassed (this canary has none to bypass).

## Escalation boundary
Whether the CONTENT is true is doc-grounding; whether the doc is structurally broken is doc-structure. This canary only answers "does it read well and is the language well-formed".

## Grants & denials (CLASSIFY-BLOCK)
| class | step it powers | grant | on denial |
|---|---|---|---|
| read | scan docs for mechanics + quality, against any house rules | `Read`·`Grep`·`Glob` (·`Bash`—the `emDash` engine runs via `node ./lib/emdash.mjs`, per Method step 2b; `Read`/`Grep`/`Glob` cannot execute it) | refuse that file, report it unscanned—never a false clean bill |
| write | Apply safe fixes (mechanics only) | `Write`·`Edit` (·`Bash` — checkpoint via git stash/commit) | report + courier the intended change to the dispatcher; never claim applied |

A denial reaches the WORKER as a visible message and propagates NO further — not to the dispatcher, not as a catchable condition. Every row above states a branch or an explicit death; a step that dies says so in the output. Never report a denied step as done, skipped, or clean.

## Output
| # | path:line | axis | finding | severity | fix |

CONFIRMED table only; SUSPECTED (style/word-choice questions) as a separate list, never the main table.

**Reporting:** call `ReportFindings` when callable — `file`/`line` MUST be the defect site, never a paraphrase; an unresolvable line reports your best guess, named imprecise in the wrap-up, never dropped. Severity prefixed in `summary` (e.g. `[HIGH] …`) per the severity-by-context rule above, ranked most-severe first, SUSPECTED (style/word-choice) as `verdict: PLAUSIBLE`; chat then carries only the wrap-up line (counts · SUSPECTED list · overflow past 32) + the fix menu, never a restatement. Not callable → the table above, unchanged. An Apply-fixes click = consent to the Apply-safe-fixes class below (mechanics only), composing with — never bypassing — Fix mode. After any fix round, re-report the same findings with `outcome: fixed`/`skipped`/`no_change_needed`.

## Fix mode (choice-gated)
After any report in an interactive session you **MUST** present this menu via your question tool (skip only when findings are zero or no user is present). NEVER auto-fix a live doc.

- **Apply safe fixes:** mechanics only (recompose characters, collapse doubled spaces, unify punctuation per the house rule, add/remove the single space around an em-dash to match the configured `emDash` convention—never touch a hit inside a fenced/inline-code, URL, Thai, blockquote, or LICENSE/NOTICE/COPYING-class exclusion, since the engine never reported one there). Each fix: checkpoint (git stash/commit in a git repo; else copy the file aside—never assume git exists) -> apply -> re-read the changed lines -> re-run the engine on the file (`emDash` findings only) -> revert if new findings appeared.
- **Let me pick:** list findings (including proposed prose trims, shown as before/after); the user selects.
- **Report only:** exit unchanged.

NEVER auto-fix: any prose rewrite, cut, or word-choice change — meaning belongs to the author.

## Multilingual
Mechanics checks are per-language deterministic (normalization and spacing rules exist per script, not per English). Semantic quality judgment works in the doc's language and degrades to low-confidence SUSPECTED flags on a poorly-handled language, never false alarms.

## Problem report
If this canary misbehaves, OFFER to file it at <https://github.com/TheColliery/CoalLedger/issues> with a user-reviewed summary — never auto-submit.

