The Writing Rules Engine
At the heart of Writan is a structured, extensible library of writing principles drawn from respected authorities. Each principle is codified as a testable rule with detection logic, severity weighting, and attribution to its source.
What’s Implemented
The mechanical tier is fully implemented with 27 rules and client-side detectors:
- Rules are defined in
lib/rules/registry.ts - Detection runs in
lib/rules/analyzer.tsviaanalyzeNode() - Individual detectors live in
lib/rules/detectors/(passive voice, adverbs, dialogue tags, sentence length, dead metaphors, etc.) - Rule enable/disable is managed by
ruleConfigStore(persists to localStorage) - Violations create orange craft pips — one pip per rule per node
The craft tier (Claude-powered analysis) is defined in the rule schema but not yet implemented.
Rule Schema
Each rule contains:
| Field | Description |
|---|---|
id | Unique rule identifier (e.g. ORW-001) |
authority | Source author |
source | Specific work the rule derives from |
category | word-choice / sentence-structure / economy / voice / narrative-craft / dialogue / rhythm / pacing |
tier | mechanical (NLP-detectable) or craft (AI-assisted) |
severity | 1–10 weighting (10 = highest priority violation) |
detection | nlp / heuristic / ai / statistical |
description | Human-readable explanation |
bad_example | Example of the violation |
good_example | Corrected version |
remediation | Guidance for fixing |
Source Authorities
| Authority | Key Work | Rules Implemented |
|---|---|---|
| George Orwell | Politics and the English Language | 6 (ORW-001 to ORW-006) |
| Stephen King | On Writing | 4 (KING-001 to KING-004) |
| Strunk & White | The Elements of Style | 2+ (SW-001, SW-002…) |
| Kurt Vonnegut | 8 Rules for Writing | Defined |
| Ursula K. Le Guin | Steering the Craft | Defined |
| Ernest Hemingway | Iceberg Theory | Defined |
| Elmore Leonard | 10 Rules of Writing | Defined |
| Samuel R. Delany | About Writing | Defined |
How Analysis Works
When prose is saved in the editor, analyzeNode() runs:
- Extract text from TipTap JSON via
extractPlainText() - Split sentences via
splitSentences() - Run all enabled detectors — each returns
RuleViolation[] - Group by rule ID — one pip per rule per node (not per violation)
- Diff against existing pips — create new pips, delete pips for rules that no longer fire
This means pips are self-healing: fix a passive voice issue and the pip disappears on next save.
Example: ORW-001 — No Dying Metaphors
Authority: George Orwell Category: word-choice | Tier: mechanical | Severity: 7
Description: Avoid metaphors so familiar they have lost their evocative power.
Bad: She was the Achilles’ heel of the operation, a hotbed of bad decisions. Good: She was the weakness no one wanted to name — the one flaw the whole operation pivoted on.
Implemented Detectors
Detectors in lib/rules/detectors/ include:
passive-voice.ts— flags passive constructionsadverbs.ts— measures adverb densitydialogue-adverbs.ts— flags adverbs after dialogue attributiondialogue-tags.ts— flags non-”said” dialogue verbsdying-metaphors.ts— matches against curated listfalse-limbs.ts— noun-verb constructions that replace simple verbspretentious-diction.ts— unnecessarily complex vocabularyneedless-words.ts— padding phrasessentence-length.ts— flags overly long sentencessentence-variation.ts— measures rhythm varietyvocabulary-complexity.ts— readability metrics- And more…
Full Ruleset
The complete ruleset specification (45+ rules across 8 authorities) is in docs/writan-writing-rules-v1.1.md.