Technical DocumentationThe Writing Rules Engine

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.ts via analyzeNode()
  • 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:

FieldDescription
idUnique rule identifier (e.g. ORW-001)
authoritySource author
sourceSpecific work the rule derives from
categoryword-choice / sentence-structure / economy / voice / narrative-craft / dialogue / rhythm / pacing
tiermechanical (NLP-detectable) or craft (AI-assisted)
severity1–10 weighting (10 = highest priority violation)
detectionnlp / heuristic / ai / statistical
descriptionHuman-readable explanation
bad_exampleExample of the violation
good_exampleCorrected version
remediationGuidance for fixing

Source Authorities

AuthorityKey WorkRules Implemented
George OrwellPolitics and the English Language6 (ORW-001 to ORW-006)
Stephen KingOn Writing4 (KING-001 to KING-004)
Strunk & WhiteThe Elements of Style2+ (SW-001, SW-002…)
Kurt Vonnegut8 Rules for WritingDefined
Ursula K. Le GuinSteering the CraftDefined
Ernest HemingwayIceberg TheoryDefined
Elmore Leonard10 Rules of WritingDefined
Samuel R. DelanyAbout WritingDefined

How Analysis Works

When prose is saved in the editor, analyzeNode() runs:

  1. Extract text from TipTap JSON via extractPlainText()
  2. Split sentences via splitSentences()
  3. Run all enabled detectors — each returns RuleViolation[]
  4. Group by rule ID — one pip per rule per node (not per violation)
  5. 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 constructions
  • adverbs.ts — measures adverb density
  • dialogue-adverbs.ts — flags adverbs after dialogue attribution
  • dialogue-tags.ts — flags non-”said” dialogue verbs
  • dying-metaphors.ts — matches against curated list
  • false-limbs.ts — noun-verb constructions that replace simple verbs
  • pretentious-diction.ts — unnecessarily complex vocabulary
  • needless-words.ts — padding phrases
  • sentence-length.ts — flags overly long sentences
  • sentence-variation.ts — measures rhythm variety
  • vocabulary-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.