FLESHNOTE / DOCS / NLP & INTELLIGENCE / THE JANITOR AUDITOR

The Janitor: Ambient Prose Auditor

9 ANALYZERS

Technical architecture for FleshNote's non-blocking background auditor: request lifecycle, the 9-analyzer pipeline, Show Don't Tell confidence scoring, and fuzzy radius offset mitigation.

Request Lifecycle & Ambient Triggering #

The Janitor operates as a silent background agent. It never interrupts the writer's typing flow and executes strictly on non-blocking background threads:

Editor.jsx / FleshNoteIDE.jsx β€” Trigger Conditions
// 1. Boundary Trigger: Fires every 100 words drafted
const currBoundary = Math.floor(words / 100);
if (currBoundary > prevBoundary) {
  triggerJanitorAnalysis();
}

// 2. Inactivity Trigger: Fires after 10 seconds of idle typing
clearTimeout(janitorInactivityTimer);
janitorInactivityTimer = setTimeout(() => triggerJanitorAnalysis(), 10000);

When triggered, the frontend posts the chapter's raw TipTap HTML to POST /api/project/janitor/analyze. The backend strips HTML while preserving character offsets, extracts word boundaries, routes the text to the matching language analyzer (janitor.py, hun_janitor.py, or pol_janitor.py), and returns a structured list of actionable suggestion cards.

The 9-Analyzer Pipeline #

Each analysis pass executes 9 dedicated linguistic and worldbuilding checks:

Analyzer Runic Token Target & Architectural Logic Cap
1. Link Existing
link_existing
ᚠ Matches untagged character, location, or lore names against the SQLite database using regex boundary search. Skips text already enclosed in existing TipTap spans. 5 cards
2. Create Entity
create_entity
ᚒ Runs spaCy NER (Named Entity Recognition) over the first 5000 characters. Maps PERSON β†’ Character, GPE/LOC/FAC β†’ Location, and ORG β†’ Lore. 5 cards
3. Shorthand Alias
alias
ᚦ Detects multi-word entity parts (β‰₯ 4 chars) appearing standalone without the full entity name nearby (>200 chars), suggesting formal alias creation. 3 cards
4. Spellcheck Typo
typo
ᚱ Runs tokenized words through localized phunspell (Hunspell) dictionaries. Automatically whitelists all registered project entities and aliases. 3 cards
5. Weak Synonyms
synonym
α›‹ Matches overused weak words ("walked", "said", "very", "got") and queries NLTK WordNet synsets for vivid lexical alternatives. 2 cards
6. Weak Adverbs
weak_adverbs
α›— Flags adverb-verb pairings that signal lazy telling (e.g. "walked slowly", "said quietly"). Uses POS tagging and language suffix rules (-ly in EN, -an/-en/-ul/-ΓΌl in HU). 5 cards
7. Passive Voice
passive_voice
α›ˆ Flags passive constructions: English auxpass dependency tags; Hungarian verbal adverbs with -va/-ve suffixes (hatΓ‘rozΓ³i igenΓ©v); Polish byΔ‡/zostaΔ‡ auxiliaries. 3 cards
8. Show, Don't Tell
show_dont_tell
α›š Executes a 4-detector pipeline measuring abstract exposition, emotional labelling, and POV camera filtering outside of dialogue blocks. 5 cards
9. Sentence Pacing
pacing
ᚫ Compares sentence-initial words across 3 consecutive sentences using spaCy sentence segmentation. Flags monotone repetitive openings. 2 cards

Show, Don't Tell: The 4 Detectors #

Before running the detectors, the engine strips all dialogue sentences to avoid falsely flagging spoken character conversation (excluding quotation marks "..." in English/Polish and em-dashes β€” ... in Hungarian).

Detector Pattern Detected EN Confidence HU / PL Confidence
emotion_label Linking verb (be, feel, seem) + emotion adjective (furious, anxious) as an adjectival complement (acomp). Exempts physical states (tall, dead, born). 0.85 0.75
filter_verb Perception verb (see, hear, notice, watch) with both subject and object clauses β€” filtering narrative immediacy through a sensory lens. 0.60 0.50
realize_verb Cognitive verb (realize, understand, know, decide) with a complement clause (ccomp / xcomp). 0.65 0.55
adverb_emotion Speech verb as syntactic root + emotion adverb modifier ("she said angrily", "he shouted furiously"). 0.75 0.65

Authors can calibrate the sensitivity slider in Project Settings β†’ Janitor (janitor_sdt_confidence from 0.30 to 0.90, default 0.50) to trade precision for recall.

Fuzzy Radius Offset Mitigation #

Because TipTap ProseMirror calculates node distances without counting invisible inter-paragraph \n separators, raw character coordinates from Python's plain-text scan will experience slight coordinate drift in large multi-page chapters.

To ensure 100% click-to-highlight accuracy, `JanitorPanel.jsx` implements a Fuzzy Radius Search:

  1. The frontend receives the backend's approximate char_offset coordinate.
  2. It scans neighboring ProseMirror text nodes within a safe radius (Β±150 characters).
  3. It executes an exact string match for matched_text, anchoring the visual pulse highlight and one-click replacement directly to the active DOM element.
On This Page