StoryForge has three validation layers:
| Layer | When | Mechanism | Who reacts |
|---|---|---|---|
| Pre-Write | manually before writing | Tool calls return structured JSON briefs / sanity checks | chapter-writer / rolling-planner |
| Pre-Save (hook) | every Write/Edit on draft.md, characters/*.md, people/*.md |
exit 2 β tool call rejected | PostToolUse hooks (validate_chapter, validate_character) |
| Post-Draft (skill) | manually invoked after draft / before revision | Markdown reports, interactive fixes | User decides |
The Pre-Write layer provides the model with structured data BEFORE the first word is written β get_chapter_writing_brief bundles 14 data sources (incl. pov_character_inventory and pov_character_state) for chapter-writer, verify_tactical_setup flags walking-order issues, get_review_brief bundles project-state metadata for chapter-reviewer, and get_continuity_brief assembles the full book state for continuity-checker. The Pre-Save layer is the production gate: no write attempt lands on disk without validation. The Post-Draft layer is the deep review: scan whole books in one pass, detect cross-chapter patterns, qualitative judgment.
| Gate | Layer | Skill / Hook |
|---|---|---|
| Chapter Writing Brief | Pre-Write (keystone) | MCP get_chapter_writing_brief |
| Tactical Setup Verifier | Pre-Write | MCP verify_tactical_setup |
| Pre-Scene Logic Audit | Pre-Write (per scene / chapter) | chapter-writer Step A1b / 2c |
| Recent Chapter Timelines | Pre-Write | MCP get_recent_chapter_timelines |
| Review Brief | Pre-Review (chapter-reviewer) |
MCP get_review_brief |
| Continuity Brief | Pre-Check (continuity-checker) |
MCP get_continuity_brief |
| PostToolUse Validator | Pre-Save (automatic, 7 scanners) | validate_chapter.py / MCP validate_chapter |
| PostToolUse Character Validator | Pre-Save (automatic, structural + recommended sections) | validate_character.py (audit H-1, PR #180) β see Character Anatomy |
| Voice Checker | Post-Draft / Prose | voice-checker |
| Chapter Reviewer | Post-Draft / Single chapter (28 points + 20a POV boundary) | chapter-reviewer |
| Chapter Humanizer | Post-Draft / Single chapter (AI-construction scan: 8 Section-11 shapes + 55 vocabulary hits; interactive fixes in author's voice) | chapter-humanizer |
| Chapter Proofreader | Post-Draft / Single chapter (spelling, grammar, punctuation β explanations in author's native language) | chapter-proofreader |
| First-Chapter Gate | Post-Draft / Chapter 1 (stricter) | in chapter-reviewer |
| Manuscript Checker | Post-Draft / Entire book (16 categories) | manuscript-checker |
| Cross-Chapter Timeline Validator | Post-Draft / Timeline drift | MCP validate_timeline_consistency |
| Continuity Checker | Post-Draft / Timeline / Travel / Canon | continuity-checker |
| Emotional Truth Pass | Post-Draft / Memoir chapters (before chapter-reviewer) | emotional-truth-prompt |
| Memoir Ethics Gate | Pre-Export / Memoir only (mandatory) | memoir-ethics-checker |
The quality gates are not optional. Books that skip them sound like AI output β even with a strong author profile. The gates are the second protective layer.
All checker MCP tools return a unified GateResult:
| Field | Type | Content |
|---|---|---|
status |
"PASS" | "WARN" | "FAIL" |
Machine-readable overall verdict |
reasons |
list[str] |
Human-readable reasons for the verdict |
findings |
list[Finding] |
Structured findings with file:line references |
metadata |
dict |
Checker-specific extras (counts, paths, ...) |
Aggregation: FAIL > WARN > PASS. validate_book_structure(book_slug) and run_pre_export_gates(book_slug) combine multiple gate results into a single aggregated GateResult.
Structured data briefs complement the hook-based validation. Instead of 16 prose-prereq-loads in chapter-writer, a single MCP tool returns a deterministic JSON.
mcp__storyforge-mcp__get_chapter_writing_brief(book_slug, chapter_slug) is the architectural keystone. Bundles 14 data sources into a single structured JSON that chapter-writer consumes via one tool call:
chapter (number, title, status, pov_character)pov_character (resolved name)story_anchor (current + previous chapter, relative-phrase mapping)recent_chapter_timelines (last 3 review-or-later chapters as intra-day grids)recent_chapter_endings (last paragraph of the previous 3 drafts)characters_present (full profiles + optional knowledge + tactical)pov_character_inventory (deterministically extracted POV character inventory; priority: frontmatter > timeline_regex > draft_heuristic > none; items with source pointers like chapter:26-the-basement:timeline:~12:55; non-empty warnings mean: ask the user, do not invent)pov_character_state (4 physical categories: clothing, injuries, altered_states, environmental_limiters; each category extracted independently with its own extraction_method; outline-aware warnings β only fire when chapter outline references the missing category; consumed by Pre-Scene Logic Audit category 5)rules_to_honor (book CLAUDE.md ## Rules, severity-classified as block/advisory)callbacks_in_register (book CLAUDE.md ## Callbacks)banned_phrases (deduplicated book + author + global, max 50)recent_simile_count_per_chapter (for simile-discipline scaling)tone_litmus_questions (from plot/tone.md)tactical_constraints (auto-active on combat/travel outline detection, otherwise null)review_handle (from ~/.storyforge/config.yaml)errors (component β error map, defensive composition)Defensive composition: Every sub-component is wrapped in try/except. A single failure source lands as a {component, error} entry in errors, the brief still ships with partial data. Determinism: same input β identical brief, including errors ordering.
Fallback for legacy books: Older Blood & Binary chapters encode POV/title in a markdown ## Overview table instead of YAML frontmatter. The brief parses both formats.
Skill size reduction: chapter-writer SKILL.md from 301 β 175 lines. Prereqs: 16 numbered prose-loads β 9 entries.
When the book is part of a series, every entry in characters_present is enriched with a series_evolution field that surfaces cross-book context to the chapter-writer.
characters_present[*].series_evolution payload shape:
"series_evolution": {
"tracker_slug": "kael",
"current_book_phase": "B2 Moonrise (geplant)",
"previous_book_end": "Mit Theo zusammen, Sera tot...",
"current_book_plan": "Trauernder Bruder, Macht-Asymmetrie kippt...",
"relationships_evolution": "- **Theo:** Liebe β ..."
}
The field is null when:
The field is always present on every character payload β downstream consumers can rely on key existence and check for null to distinguish "no series context" from "missing field".
Source: tools/state/loaders/series.py::build_series_evolution_for_character(). Source priority for current_book_plan: tracker geplant > ende > start (most-future-facing slot wins). For B1 chapters, previous_book_end is empty string (no prev band).
Cross-link: see Character Anatomy β Series Lifecycle for the full workflow.
Issue reference: D-3 of Epic #195.
mcp__storyforge-mcp__verify_tactical_setup(book_slug, scene_outline_text, characters_present) checks combat and travel scenes against optional tactical: profiles of the participating characters:
tactical:
protector_role: false # actively protects others
protected_role: true # needs protection in combat
combat_skill: none # none|low|medium|high|elite|unknown
movement_lead: false # tends to take point
movement_rear: false # tends to bring up the rear
vulnerable_to: [daylight, silver]
carries: [knife]
5 heuristic rules:
movement_lead: true in profile β warnPosition detection uses sentence-bounded preceding-name attribution. "Theo walked flanked by Kael at point and Viktor at the rear" β Theo=middle, Kael=lead, Viktor=rear.
Always returns β₯3 questions-for-writer as a pre-write checklist β even when passes: true. Example:
{
"passes": false,
"warnings": [
{"severity": "warn", "message": "Theo (protected_role: true, combat_skill: none) is in rear position. Protectors (Kael, Viktor, Dom) should flank or trail."}
],
"questions_for_writer": [
"Who is closest to Theo at all times?",
"Who scouts ahead?",
"What is the formation if they need to break and run?"
],
"detected_positions": {"Theo": "rear", "Kael": "lead", "Viktor": "middle", "Dom": "middle"}
}
chapter-writer Step A2 and rolling-planner Step 4b call the tool gated on combat/travel keyword detection (is_tactical_scene).
The Pre-Scene Logic Audit is a mandatory structured block that chapter-writer emits to chat before every scene (Mode A: Step A1b) or once per chapter (Mode B: Step 2c) β the structural counter to context-pressure invention (Issue #158: seven independent invention errors in a single ~960-word scene).
A model that has just written four scenes in a row will not reliably re-consult loaded sources unless forced to. The audit block is that enforcement.
Five mandatory categories β each answered in one sentence with source citation. Gaps are surfaced explicitly, never papered over:
| Category | Source | On gap |
|---|---|---|
| 1. Inventory (POV character) | Brief pov_character_inventory (frontmatter priority, source pointers) |
Non-empty warnings β ask the user, do not invent |
| 2. Geography | world/setting.md (travel matrix), plot/timeline.md, recent_chapter_timelines |
Model the route mentally before any movement verb |
| 3. Character biography & relationships | characters/{slug}.md, brief canon_brief (pov_relevant_facts + changed_facts) β for the unfiltered scope-window list call standalone MCP get_canon_brief() |
Source-free claim β ask |
| 4. Banned phrases + author tics | Brief banned_phrases, author profile recurring_tics / donts |
Replan the beat before prose is written |
| 5. Sensory plausibility | Brief pov_character_state (clothing, injuries, altered_states, environmental_limiters) |
extraction_methods[cat] == "none" + beat depends on it β ask |
Example (category 1):
"Theo (Ch26 ~12:55, source: chapter:26-the-basement:timeline:~12:55): compass, silver knife, no-signal phone, half a power bar, mission jacket. Pamphlet NOT on his person."
Example (category 5):
"
pov_character_state.clothingβ tactical boots (frontmatter); planned beat 'the steps were colder than he expected' β boots prevent direct stone-cold sensation β rewrite as 'He gripped the stone railing β cold enough that even through gloves he felt it.'"
mcp__storyforge-mcp__get_recent_chapter_timelines(book_slug, n=3) loads the last N review-or-later chapters as JSON. Filter: rank β₯ 2 (drafts/outlines are skipped). Prevents cross-chapter cascade drift, the named beta-feedback symptom from Blood & Binary Ch 22.
For chapter-writer, this tool provides structured intra-day grids. continuity-checker uses get_continuity_brief(book_slug) instead β which loads ALL chapter timeline grids (not just the last 3 review-or-later) plus canonical calendar, travel matrix, canon log, and character index in a single call.
mcp__storyforge-mcp__update_character_snapshot(book_slug, pov_slug, snapshot_json, book_category) persists the end-of-chapter state of the POV character back to characters/{pov_slug}.md (fiction) or people/{pov_slug}.md (memoir) as YAML frontmatter fields.
Fields written:
current_inventory β items the character carries at chapter endcurrent_clothing β clothing / gear on bodycurrent_injuries β wounds, pain, mobility limitationsaltered_states β fatigue, intoxication, hunger, shockenvironmental_limiters β sensory dampeners (mask, helmet, ear protection)as_of_chapter β chapter slug of the last updateWhy this matters: pov_character_inventory and pov_character_state use frontmatter as their highest extraction priority. Without write-back, each new chapter brief degrades to timeline_regex or draft_heuristic extraction β fragile sources that miss silent state changes. A 30-second snapshot dialog at chapter close keeps the brief sharp for all subsequent chapters.
Trigger (chapter-writer Step 7.8): The skill extracts the end state from the Chapter Timeline block just written, proposes a snapshot, waits for confirmation or corrections, then persists. Only on Review / Final closes β not on mid-chapter Draft saves.
mcp__storyforge-mcp__get_review_brief(book_slug, chapter_slug) assembles the project-state metadata that chapter-reviewer needs into one structured JSON brief β replacing 6+ direct file reads:
chapter_timeline β intra-day time grid for the target chapter (start/end/scenes)previous_chapter_timeline β same for the preceding chapter (cross-chapter time checks)canonical_timeline_entries β parsed plot/timeline.md events (day/date/chapter/events)travel_matrix β parsed world/setting.md Travel Matrix rows (from/to/distance/travel_time)canon_log_facts β parsed plot/canon-log.md facts with status (ACTIVE / CHANGED)tonal_rules β non-negotiable rules, litmus test, banned patterns, warning signs from plot/tone.mdactive_rules β book CLAUDE.md ## Rules with severity (block / advisory)active_callbacks β book CLAUDE.md ## Callback Register itemsChapter draft texts are intentionally NOT in the brief β they are the data being reviewed, not project-state metadata (ADR-0001: data-briefs-over-prompt-instructions).
Defensive composition: Same _Recorder pattern as the Chapter Writing Brief β every sub-component wrapped in try/except. Non-empty errors means some files were missing or unreadable; the brief ships with partial data for graceful degrade.
mcp__storyforge-mcp__get_continuity_brief(book_slug) assembles what continuity-checker needs β replacing direct reads of timeline, setting, canon log, and character files:
canonical_calendar β parsed plot/timeline.md events (story_day/real_date/chapter_slug/key_events)travel_matrix β parsed world/setting.md Travel Matrix rows (from/to/distance/transport/travel_time/notes)canon_log_facts β parsed plot/canon-log.md facts with status (ACTIVE / CHANGED / SUPERSEDED) and domaincharacter_index β all character files as flat list (slug/name/role/description)chapter_timelines β intra-day timeline grids for ALL chapters (any status) β source of truth for chapter start/end anchorsUnlike get_recent_chapter_timelines (which filters to review-or-later chapters), the continuity brief includes all chapters β drafts and outline-stage β because continuity-checker scans the full manuscript.
Chapter draft texts are intentionally NOT in the brief β they are the data being checked, not project-state metadata (ADR-0001).
The most important quality gate. Runs automatically after every Write/Edit/MultiEdit on **/chapters/*/draft.md. Reads the Claude Code JSON payload from stdin, scopes itself strictly to chapter drafts; other files pass through silently.
A second PostToolUse hook β validate_character.py β runs alongside the chapter validator and scopes itself to **/characters/*.md and **/people/*.md files. Same JSON-payload protocol, same exit-code semantics, different schema. Documented in detail at Character Anatomy.
| Severity | Effect | Behavior |
|---|---|---|
block |
Hook returns exit code 2 | Tool call is rejected, stderr fed back to model, model must fix |
warn |
Hook returns exit code 0 | Findings land in output, do not block |
info |
Hook returns exit code 0 | Hint without pressure (e.g. knowledge profile missing) |
Default is strict. A book can switch to warn via the CLAUDE.md frontmatter:
---
linter_mode: warn
---
# My Book
In warn mode, even block-severity findings do not reject β all findings as stderr warnings only. Emergency override when, for example, a pattern is too aggressive and you need to keep writing.
CLAUDE.md banlist (severity: block)Source: projects/{book}/CLAUDE.md section ## Rules with backtick patterns
Per-scene counter: When a rule declares max N per chapter, this scales proportionally to current draft length. With a 900-word scene and 3200-word chapter target, max 3 per chapter yields a per-scene limit of 1.
Example output:
[BLOCK] draft.md line 12: phrase 'kind of X that Y' appears 5 times
(scaled scene limit: 1; chapter cap: 3; current draft: 904w of 3200w
target). Cut at least 4. line 3: β¦She walked the kind of corridor
that smelledβ¦; line 5: β¦He noticed the kind of silence that followsβ¦
Recognized limit phrasings: max N per chapter, max N-M per chapter (upper bound), maximum N per chapter, max of N per chapter, limit to N per chapter, German max N per kapitel.
Source: ~/.storyforge/authors/{slug}/vocabulary.md sections ### Absolutely Forbidden, ### Forbidden Hedging Phrases, ### Forbidden Emotional Tells, ### Forbidden Structural Patterns
Slug resolution: auto-derived from book CLAUDE.md ## Book Facts (- **Author:** Ethan Cole)
Inflection matching: Single words with silent e (delve) also match delved, delving, delves. Multi-word phrases match exactly.
Example output:
[BLOCK] draft.md line 23: Banned by author voice
(author-vocab (Absolutely Forbidden)): 'delve'
Hardcoded patterns: Ch \\d+, callback(s), as established, echoes the earlier, foreshadow*, calls back to, parallels|mirrors (the|his|her|their) (earlier|previous)
HTML comments excluded β outline notation in <!-- ... --> stays allowed
Example output:
[BLOCK] draft.md line 47: meta-narrative phrase 'Ch 15' (chapter
reference) β name the event or character instead of pointing to
the chapter number
Deliberately excluded: beat, set piece, bare parallels β too many legitimate prose uses without narration-vs-dialog awareness.
Reads ## Chapter Timeline start/end from current chapter README
Maps relative phrases (yesterday, tomorrow, last week, an hour ago, last night, etc.) to concrete story dates
Example output:
[WARN] draft.md line 47: phrase 'yesterday' implies Mon Dec 23 ~19:30
(chapter starts Tue Dec 24 ~19:30). Verify against plot/timeline.md.
Deliberately warn-only, not block. Block-on-implied-event-conflict (phrase references an event in timeline.md far from the implied date) needs semantic event matching that is too false-positive-prone without LLM.
Source: reference/craft/anti-ai-patterns.md section ### Heavily Flagged Words and Phrases
~50 curated patterns (delve, tapestry, vibrant, embark, etc.)
Example output:
[WARN] draft.md line 8: AI-tell 'tapestry' found (3 occurrences)
Mirror in the manuscript-checker: Since #216 the same Section 1 vocabulary is also scanned by /storyforge:manuscript-checker and surfaces under ai_tell_violation (severity medium). The hook stops bypassed writes; the manuscript-checker catches them on the final pre-export sweep.
Measures standard deviation of sentence length across the whole scene
At std_dev < 4 β warning (text reads too uniform β AI-typical)
Example output:
[WARN] draft.md: Low sentence length variance (std_dev=2.3) β text
may sound AI-generated. Vary sentence lengths more.
Purpose: Prevents close-third POV drift where narration attributes domain knowledge the POV character does not have. Beta-feedback example: "blood smells when it has been on the ground for a while in cold air" β written from Theo's IT-guy POV.
Sources: POV character from chapter.yaml / Overview table, knowledge profile from characters/{pov-slug}.md (optional knowledge: block), vocabulary from reference/craft/knowledge-domains/{domain}.md
Strips dialog (straight + curly quotes) before scanning β narrator-knowledge gate, not character speech
Word-boundary match (regression-tested): pea does NOT match inside appeared, speak, ahead. Smoke test against Blood & Binary Ch 20-22 went from 6 false positives to 0 after the fix.
Knowledge schema in character profile:
knowledge:
expert: [it, programming, networking, devops]
competent: [photography, brewing_coffee]
layperson: [psychology, history, philosophy]
none: [forensics, ballistics, medicine, tactical_combat, automotive_repair]
5 seed domains under reference/craft/knowledge-domains/: forensics, tactical_combat, medicine, ballistics, automotive_repair β community-extensible via PR
Free-form domain keys (e.g. learned_from_kael) are treated as competent
Flags phrases from domains the POV character has declared none or layperson for
Example output:
[WARN] draft.md line 47: POV BOUNDARY: 'blood smells when' (domain: forensics,
Theo Wilkons knowledge: none). Move into dialog by an expert, reframe as
lay observation, or cut.
Deliberately warn-only, not block. POV calls are nuanced; false positives acceptable, false negatives on the named beta-feedback case (Theo's forensic knowledge) are not.
chapter-reviewer sub-point 20a surfaces these warnings in the 28-point review.
Write attempt of Theo clocked the door in a Blood-&-Binary scene:
StoryForge linter blocked this write:
[BLOCK] draft.md line 12: Banned by author voice
(author-vocab (Absolutely Forbidden)): 'clocked'
[BLOCK] draft.md line 12: Banned phrase from book CLAUDE.md: 'clocked'
Plus 3 non-blocking warnings:
[WARN] draft.md line 8: phrase 'yesterday' implies Mon Dec 23 ~19:30
(chapter starts Tue Dec 24 ~19:30). Verify against plot/timeline.md.
[WARN] draft.md line 15: AI-tell 'tapestry' found (1 occurrence)
[WARN] draft.md line 47: POV BOUNDARY: 'lividity' (domain: forensics,
Theo Wilkons knowledge: none).
Fix the blocking issues and try again. Set `linter_mode: warn` in the
book's CLAUDE.md frontmatter to override.
The model receives this message in stderr and must fix the block-severity issues before the next write is accepted.
For the hook and soft warnings to coexist cleanly, a clear markup convention applies in ## Rules bullets:
| Markup | Meaning | Who reacts |
|---|---|---|
`phrase` (backticks) |
Hard-block pattern | PostToolUse hook β exit 2 |
*phrase* (italics) |
Heuristic marker for manual review | manuscript-checker (soft) |
"phrase" (double quotes) |
Examples, replacements, advisory | Read-only hint for the model |
More details in the StoryForge main page.
A dedicated layer for editing and auditing tools that operate on the contents of the book CLAUDE.md ## Rules block. Three MCP tools plus an interactive skill β they complement the PostToolUse hook and the Manuscript Checker without competing with them.
When to use this layer:
| Tool | Purpose | Returns |
|---|---|---|
list_book_rules(book_slug) |
Inventory of all rules in the RULES block | [{index, title, raw_text, has_regex, has_literals, extracted_patterns}] |
lint_book_rules(book_slug) |
Bulk audit against the pattern-extraction contract | {rules_total, issues: [{rule_index, title, warnings, extracted_patterns}]} |
update_book_rule(book_slug, rule_match, new_text, validate=True) |
Edit or delete a rule, with re-lint | {found, changed, rule_index, old_text, new_text, warnings, extracted_patterns} |
All three operate strictly inside the <!-- RULES:START --> / <!-- RULES:END --> markers. Static rules above the block remain invisible β they are template boilerplate, not user-managed.
append_book_rule(book_slug, text, validate=True) lints on insert too. Default validate=True β new rules with scanner-blind shapes report themselves at append time, not only after the manuscript-checker scratches its head.
| Warning | When it fires | Hint |
|---|---|---|
italic_examples_with_ban_cue |
Ban cue + *"phrase"* examples in the rule |
Italic-wrapped examples are invisible to the scanner β use backticks |
mixed_positive_negative_quotes |
Ban cue + two or more "..." phrases |
Scanner extracts EVERY quoted phrase as a ban, including positive examples |
bracket_placeholder |
Backtick body contains [noun], [verb], [subj] etc. |
Scanner reads it as a character class, not a placeholder β use \w+ |
scanner_extracts_nothing |
Ban cue without backticks and without quoted phrase | The rule is effectively dead; add backticks AND document an alternative |
The lint uses the same pattern extractor as the manuscript-checker β what the lint flags, the scanner would silently drop. Lint logic does not drift away from scanner logic.
The same contract applies on the author-profile side. write_author_discovery(validate=True) runs a section-aware linter over each Recurring Tic, Don't, or Style Principle bullet and attaches warnings + extracted_patterns to the MCP response. Five codes:
| Section | Code | When it fires |
|---|---|---|
donts |
mixed_positive_negative_italics |
Italics on both sides of a recommendation marker (Render, Instead:, β, ...) β the post-marker italics are silently NOT extracted as bans. |
donts |
mixed_positive_negative_quotes |
Multiple double-quoted phrases under a ban cue β every quote before the marker extracts as a ban. |
donts |
scanner_extracts_nothing |
Ban cue without any backtick / quoted / italic β the rule won't enforce because the scanner sees no pattern. |
recurring_tics |
bold_title_unscannable |
Non-ASCII title + no body pattern β the title-text fallback would compile a non-English rule name that never matches English prose. |
donts, recurring_tics, style_principles |
bracket_placeholder |
Backtick body contains [word] β that's a character class, not a \w+ placeholder. |
Style Principles intentionally do NOT emit scanner-gap warnings β they are not machine-scanned by design.
The write itself is never blocked by lint warnings β it's observability so the harvest skill can surface them to the user and the user can decide to keep, edit, or rewrite.
/storyforge:rules-auditOrchestrating the three tools by hand works, but is tedious. /storyforge:rules-audit [book-slug] makes the audit interactive: scan β findings table β triage per finding β apply with re-lint β report.
Four triage options per finding:
After every write, re-lint runs. If the fix introduces new warnings, the user can iterate or accept. Final re-lint for verification, report covering fixed / cleaned / skipped / still flagged.
Rules audit never changes rules without user confirmation. Lint findings are advisory; the user owns the rule semantics.
The rules-hygiene layer is the proactive counterpart to the quote-format migration. Migration only converts "..." β `...`; the lint additionally flags shape problems that survive backtick conversion (italic examples with ban cues, mixed quotes, dead patterns).
Order for legacy books: run migrate_to_backticks first, then /storyforge:rules-audit for the remaining shape issues.
The skill /storyforge:chapter-humanizer runs after chapter-reviewer craft fixes are applied. It targets AI-construction patterns that craft review doesn't catch β structural tells embedded in sentence logic, not just vocabulary.
Pass 1 β 8 Section 11 Shapes (Elegant Abstraction Register)
| Shape | Pattern | Example |
|---|---|---|
| 11.1 Embodied Abstract Emotion | Emotion verb + body part as autonomous agent | "His hands remembered what his mind refused to admit" |
| 11.2 Trust Split | Character thinks/knows something but body won't cooperate | "He knew it was fine. His chest didn't." |
| 11.3 Silence-as-Volition | Silence is framed as a decision or act | "The silence decided for them." |
| 11.4 Possession Split | Part of character acts independently of character | "His voice found its way out before he could stop it." |
| 11.5 Latent-Property Abstraction | Abstract noun granted an activity it can't perform | "The truth of the situation hung between them." |
| 11.6 Ontological State Separation | Character split between body and mind as separate agents | "His mind catalogued exits while his body stayed very still." |
| 11.7 Backward-Negation Loop | Sentence describes what didn't happen before stating what did | "His throat closed on what it had been refusing to close on." |
| 11.8 Expository Repeat | Same idea stated twice β once abstractly, once concretely | "He was afraid. The fear had teeth." |
Pass 2 β Vocabulary Scan: 55 flagged words and constructions from the anti-AI catalog. Each hit is presented interactively: apply N / skip N / N: shorter β proposed replacements written in the author's voice.
voice-checker vs. chapter-humanizer
| voice-checker | chapter-humanizer | |
|---|---|---|
| Output | Score 0β100 across 7 dimensions | Per-hit list with proposed fixes |
| Approach | Holistic / diagnostic | Surgical / prescriptive |
| Required? | No β optional standalone | Recommended after chapter-reviewer |
| Best for | "How AI does this chapter sound overall?" | "Fix each AI tell, one by one" |
The skill /storyforge:voice-checker is not a required step in the standard workflow. Use it when you want a holistic authenticity scorecard rather than targeted fixes β or as a final sanity check before export. For surgical AI-tell removal, use /storyforge:chapter-humanizer instead.
It scans text in seven dimensions. It compares against the author profile and the built-in anti-AI pattern catalog.
What is checked:
anti-ai-patterns.mdExample output:
Vocabulary β 5 AI-Tells found
---
Line 234: "journey" (banned AI-noun)
Line 401: "tapestry of emotions" (abstract noun cluster)
Line 612: "landscape" (metaphorical, banned)
Line 823: "it's worth noting" (hedging)
Line 1205: "embraced" (AI-verb in metaphorical context)
What is measured:
Human prose: StdDev > 8 words. Mix of 3-word fragments and 35-word sentence arcs.
AI prose: StdDev < 5 words. Suspiciously uniform.
Flag rule: At least 5 consecutive sentences within a 3-word length window β warning.
Example output:
Sentence Length Analysis
---
Mean: 14.3 words
Median: 13 words
StdDev: 4.2 words (β οΈ AI-suspicious, < 5)
Flags:
- Paragraph 4: 6 consecutive sentences 12-15 words β uniform
- Paragraph 11: 4 consecutive sentences 14-17 words β uniform
Checks:
Checks:
Checks:
Checks:
Checks:
The end produces an AI-Tell Score of 0-100:
| Score | Verdict |
|---|---|
| 0-20 | Solid β ready for next stage |
| 21-40 | Acceptable β minor fixes recommended |
| 41-60 | Problematic β revise chapter |
| 61-80 | AI-suspicious β major overhaul |
| 81-100 | Practically AI-generated β rewrite from scratch |
The skill /storyforge:chapter-reviewer checks a single chapter against a 28-point checklist (plus subpoints).
| Category | Points | Focus |
|---|---|---|
| Structure | 5 | Opening, scene-sequel, arc, ending, pacing |
| Craft | 5 + 1 | Show/tell, senses, specific details, dialog, conflict, simile discipline |
| Voice | 5 + 1 | Author consistency, tone, vocabulary, rhythm, dialog voice, phrase micro-echo (14b) |
| Continuity | 5 + 1 | Canon, timeline, travel, stale references, character facts, POV boundary (20a) |
| Tonal Consistency | 5 | Dominant mode, warning signs, non-negotiable rules, litmus test, banned patterns |
| Intra-Day Timeline | 3 | Time anchor, internal consistency, cross-chapter |
Every simile and metaphor in the chapter is run through a two-question test:
Flag cases:
Author-voice bias: When the profile documents a simile-rich style (e.g. Victorian, Chandler-like), the bar is more lenient β the check enforces quality, not quantity.
Within any 3-sentence window, content words (nouns, verbs, adjectives) are checked for close-range repetition without intentional rhetorical effect.
Three flag cases:
"the locked-down quality β¦ Theo knew that quality")Excluded: Intentional repetition (anaphora, refrain, litany).
Severity: WARN by default β FAIL only if the echo derails a critical scene beat. Always suggest the simplest fix first: pronoun, synonym, or reword β not elaborate imagery.
Surfaces PostToolUse hook warnings in the review report. Per [WARN] pov_boundary finding from the hook, three remediation options:
Skip when POV character has no knowledge: block β graceful degrade.
## Chapter Review: "The Beacon" (Chapter 2)
### Structure (5 / 5)
- [x] 1. Opening hook β "The fog did not come from the sea." Strong opener.
- [x] 2. Scene-sequel flow β Three clean scene-sequel pairs
- [x] 3. Chapter arc β Arne moves from suspicion to curiosity
- [x] 4. Ending β Cliffhanger with the silver coin
- [x] 5. Pacing β Atmosphere build, then twist
### Craft (4 / 5 + subpoint)
- [x] 6. Show don't tell
- [x] 7. Sensory details (sight, sound, smell strong β no taste)
- [WARN] 8. Specific details β "the furniture" instead of concrete (line 47)
- [x] 9. Dialog quality
- [x] 10. Conflict
- [WARN] 10b. Simile discipline β 1 stacked simile paragraph (lines 201-203)
### Voice (5 / 5)
[all PASS]
### Continuity (3 / 5 + subpoint)
- [x] 16. Canon consistency
- [x] 17. Timeline accuracy
- [FAIL] 18. Travel consistency β Arne walks from harbor to lighthouse
in 15 min; Travel Matrix says 25 min.
- [x] 19. Stale references
- [FAIL] 20. Character facts β Inga described as 32; canon says 34
- [WARN] 20a. POV boundary β line 89: "lividity" (forensics, Arne knowledge: none)
β move into dialog by Pastor Thaden or reframe as
"death-marks" lay observation
### Tonal Consistency (5 / 5)
[all PASS]
### Intra-Day Timeline (3 / 3)
[all PASS]
### Phrase Echo Report
- Close-range echoes found: none
- Intentional refrains excluded: 0
## Overall: 23 / 28 β Revise before Final
Critical: Fix travel time (line 87), fix Inga age (line 134), POV boundary line 89
Polish: Specific details line 47, simile stack lines 201-203
Stricter regime for the first chapter. Runs before the 28-point list.
Set the Stage
Spotlight the Protagonist
Give Readers a Reason to Stay
The most critical (FAIL here = chapter must be revised before the next):
If one of these fails, the skill blocks further workflow.
"The lighthouse was already dark when Arne arrived."
β Tone: PASS
β Genre hint: ambiguous (could be mystery, horror, literary)
β Curiosity: PASS (why dark?)
β Killer level: 7/10
"It was a rainy autumn morning, and Arne Kruse drove to the island."
β Tone: WEAK (generic)
β Genre hint: none
β Curiosity: WEAK
β Killer level: 3/10 β revise
The skill /storyforge:manuscript-checker scans the entire book for problems that only surface during end-to-end reading.
| Category | What is captured | Severity |
|---|---|---|
book_rule_violation |
Violations of rules from per-book CLAUDE.md | Always High |
author_rule_violation |
Violations of ### Don'ts from the author profile β applies to every book by this author |
Always High |
author_vocab_violation |
Violations of vocabulary.md ### Forbidden ... sections (same source the PostToolUse hook reads) |
Always High |
writing_discovery_violation |
Violations of ### Recurring Tics in the author profile (promoted from earlier books via /storyforge:harvest-author-rules) |
Always High |
global_shape_violation |
Violations of catalog-wide shape bans from anti-ai-patterns.md Section 11 (elegant-abstraction register, applies to every author with no per-author setup) |
Medium (advisory) |
ai_tell_violation |
Hits on catalog AI-tell vocabulary from anti-ai-patterns.md Section 1 (delve, tapestry, vibrant, ... β applies to every author with no per-author setup) |
Medium (advisory) |
cliche |
~130 curated patterns of worn-out fiction phrasings | Always High |
question_as_statement |
Q-word dialog with period instead of question mark | High at β₯5 hits |
filter_word |
POV-distancing verbs (>3/1k words) | High at >6/1k |
adverb_density |
-ly adverbs (>8/1k words) |
High at >14/1k |
sentence_repetition |
Recurring 8β15 word phrases across chapters | High at β₯3 hits |
snapshot |
Static description blocks without action verbs or dialog | Medium |
callback_dropped |
Registered callbacks absent from all drafted chapters | High |
callback_deferred |
Callbacks silent for more than 10 chapters | Medium |
simile |
Cross-chapter simile repetition | High at β₯4 hits |
character_tell |
Repeated body-language tics | High at β₯4 hits |
blocking_tic |
Repeated blocking patterns ("stood up", "sat down") | High at β₯4 hits |
sensory |
Repeated sensory descriptions | High at β₯4 hits |
structural |
Repeated sentence structures | High at β₯4 hits |
signature_phrase |
Author signature phrase too often | High at β₯4 hits |
The manuscript-checker is more tolerant with the book CLAUDE.md
## Rulessection than the PostToolUse hook: it captures both backtick and double-quoted patterns (with ban cue). Intentional β soft findings tolerate 50/50 false positives because you decide as the user. Hard-block findings (in the hook) require explicit backticks.
Author-profile sources: The three
author_*categories read from~/.storyforge/authors/{slug}/profile.md(sections### Don'tsand### Recurring Ticsunder## Writing Discoveries) andvocabulary.md(all### Forbidden ...subsections). Add a rule to the author profile once β it applies to every book by that author. No more per-book duplication.Format convention for
### Don'ts: patterns either as backtick regex (`\bthe (room|silence) (received|held)\b`) or as italicized example phrases (*The room received it.*) in the same bullet as a ban cue (Never,Avoid,Don't use). The italic form matches the phrase anywhere in the manuscript β trailing sentence punctuation (.,,,!) is stripped during pattern build.Recommendation markers (#217): Italics and double-quoted phrases AFTER a marker word (
Render,Replace,Use instead,Instead:,Allowed:,Better:,Rewrite as,β,Rather:) are treated as positive examples (what to write instead) and are NOT extracted as banned patterns. Backticks remain extracted regardless of position because they encode explicit ban intent.Recurring Tics body extraction (#212): When a Recurring Tic bullet's bold title carries no quoted phrase, the loader reads scannable patterns from the bullet body β both double-quoted phrases and backticks. This lets German rule labels with English example phrases ("his hands were having a conversation with each other") scan correctly without duplicating into a Don't.
Catalog shape bans (
global_shape_violation): The elegant-abstraction shapes fromreference/craft/anti-ai-patterns.mdSection 11 (word-count meta-commentary, sentence-as-projectile, room-as-receiver, economic metaphor, body-part anthropomorphisation) are scanned automatically for every author β no per-profile copy needed. Severity is medium (advisory): the manuscript report lists hits, the hook emits awarnrather than blocking. To hard-block a shape for a specific author, copy the regex into that author'sprofile.md ### Don'ts(severity escalates toblock).Catalog AI-tell vocabulary (
ai_tell_violation): Since #216 the Section 1 vocabulary from the same catalog file (delve,tapestry,vibrant,embark, ...) is also scanned by the manuscript-checker at severity medium (advisory), in parallel to the hook'swarn-level enforcement. To hard-block a word for a specific author, add it tovocabulary.md ### Absolutely Forbidden(block severity + inflection matching).Dedup: When an author-level Don't, vocabulary entry, or Recurring Tic already blocks a hit at a given chapter+line, both catalog warnings (
global_shape_violation,ai_tell_violation) are suppressed for that exact line β no double-flagging of the same position.
Scans all chapter drafts for recurring 8β15 word phrases. Captures patterns that evade per-chapter notice by occurring once per chapter but many times across the book:
Severity: High at β₯3 cross-chapter occurrences. Uses n-gram overlap β variations sharing 8β15 consecutive words are grouped together.
Flags static description blocks β consecutive sentences with no action verbs and no dialog. Snapshots stall narrative momentum and are a common AI-prose pattern.
Detection: A block of β₯5 consecutive sentences without action verbs or dialog markers triggers a finding. Override the threshold per book in the book's CLAUDE.md:
## Linter Config
snapshot_threshold: 8
Severity: Always Medium. Some atmospheric pauses are intentional β you decide which findings to act on.
Reads the ## Callback Register in the book's CLAUDE.md and cross-references every entry against all drafted chapters:
callback_dropped (High): A callback appears in the register but has no occurrence in any drafted chapter β a promise made and never delivered.callback_deferred (Medium): A callback last appeared more than 10 chapters ago without resolution. May be intentional (slow burn) or silently dropped.Satisfied callbacks produce no findings. Use /storyforge:register-callback to maintain the register, or call verify_callbacks(book_slug) directly for a structured JSON result.
scan_manuscript MCP tool)research/manuscript-report.md)Manuscript Scan β "The Lighthouse Keeper" (34 chapters, 82,300 words)
---
Found: 127 findings
High-Severity:
- 3 Book-Rule-Violations
- 5 ClichΓ©s
- 1 Question-as-Statement-Cluster
- 4 Heavy-Filter-Word-Chapters
- 2 Heavy-Adverb-Chapters
- 7 Cross-Chapter-Repetitions
Top Offenders:
1. RULE-VIOLATION: "journey" as metaphor β 12x (ch 3, 7, 14, 19, 22)
β Per-Book CLAUDE.md bans abstract nouns
2. CLICHΓ: "his blood ran cold" β 3x (ch 2, 17, 29)
3. REPETITION: "for a moment he paused" β 18x (structural)
4. FILTER-WORDS ch 8: "saw"Γ14, "noticed"Γ8, "seemed"Γ5 (24/1k words)
5. SIMILE: "like a feather on water" β 4x ch 5, 9, 17, 23
Full Report: research/manuscript-report.md
With --interactive the checker walks through each high-severity finding:
Finding 1 / 7
---
Book-Rule-Violation: "journey" as metaphor (12 hits)
Hits:
- Ch 3, line 67: "...his inner journey..."
- Ch 7, line 142: "...the journey of memory..."
- Ch 14, line 201: "...a quiet journey..."
[...9 more]
Recommendation:
- Ch 3: Replace with "path" or concretely "drive through fog"
- Ch 7: Replace with "memory chain"
- Ch 14: Cut the sentence β redundant with previous paragraph
Action?
[a] Apply all
[c] Custom (adjust each)
[s] Skip
[q] Quit
question_as_statementFlat-delivery questions ("Who was that.") are a legitimate stylistic choice (McCarthy style). In small doses.
At scale (18+ hits) they become monotonous or look like a bug.
Per hit, the skill offers two options:
Example (B):
Before: "Who?"
After: "Who." His gaze did not move from the coin.
mcp__storyforge-mcp__validate_timeline_consistency(book_slug) cross-references chapter anchor dates and relative time phrases in draft prose against the canonical plot/timeline.md Event Calendar. It is the batch counterpart to the per-write Story-Time Anchor hook β use it to retrofit existing chapters or validate a finished draft.
For each chapter with a parseable ## Chapter Timeline section in its README:
yesterday, last week, this morning, tonight, an hour ago, etc.)draft.md with word-boundary regex β longest-phrase-first so "last week" wins over "last"plot/timeline.mdChapters without a parseable ## Chapter Timeline section are reported in missing_anchors rather than failing β use /storyforge:chapter-reviewer to add the section to the README.
plot/timeline.md with a populated ## Anchor Point table (provides the calendar year) and at least one ## Event Calendar row.calendar_built: false and no drift analysis runs.{
"book_slug": "my-book",
"chapters_checked": 22,
"calendar_built": true,
"findings": [
{
"chapter": "22-the-night-before",
"scene": "Scene 3",
"line": 78,
"phrase": "yesterday",
"implied_date": "2025-12-23",
"actual_event_date": "2025-12-15",
"drift_days": 8,
"snippet": "...four hours into the drive, Theo thought about yesterday..."
}
],
"missing_anchors": ["11-rooftop"],
"report_path": "reports/timeline-validation.json"
}
Results are also persisted to {book}/reports/timeline-validation.json after each run.
Use
/storyforge:continuity-checkerfor rule-of-thumb timeline reconstruction from prose. Usevalidate_timeline_consistencyfor precision drift detection whenplot/timeline.mdis already filled in.
The skill /storyforge:continuity-checker validates the entire book against:
plot/timeline.md) β temporal consistencyworld/setting.md) β spatial consistencyplot/canon-log.md) β fact consistencyThe skill calls get_continuity_brief(book_slug) to load all required project-state data in a single structured JSON β canonical calendar, travel matrix, canon log facts, character index, and intra-day timeline grids for ALL chapters. Chapter draft texts are loaded separately as the content under review.
Temporal conflicts:
Ch 5, line 89: "It was Wednesday"
Timeline: Day 12 = Thursday
β CONFLICT
Travel impossibilities:
Ch 8: Arne leaves the lighthouse at 14:20, arrives at the parsonage
at 14:35.
Travel Matrix: lighthouse β parsonage = 45 min
β CONFLICT (30 min short)
Character-fact drift:
Ch 2: Inga introduced as 34
Ch 11: "Inga, the 32-year-old..."
Canon Log: Inga = 34
β CHANGED/CONTRADICTED fact
Location-description drift:
Ch 3: The kitchen is "small, light blue, with only one window"
Ch 19: "...the spacious kitchen with the two large windows..."
β DESCRIPTION DRIFT
The MCP tool get_current_story_anchor(book_slug, chapter_slug) returns a structured mapping from the current chapter start to all standard relative phrases. Used live by the PostToolUse hook during writing (warn severity), but can also be invoked directly:
mcp__storyforge-mcp__get_current_story_anchor blood-and-binary-firelight 22-the-night-before
Returns, among other things:
{
"available_relative_phrases": {
"yesterday": "Mon Dec 23 ~19:30",
"tomorrow": "Wed Dec 25 ~19:30",
"an hour ago": "Tue Dec 24 ~18:30"
}
}
Prerequisite: ## Chapter Timeline in the chapter README in the format **Start:** Tue Dec 24 ~19:30.
get_recent_chapter_timelines(book_slug, n=3) returns the last N review-or-later chapters as structured intra-day grids β useful for chapter-writer to avoid cross-chapter cascade drift. The continuity-checker uses get_continuity_brief instead (all chapters, not just the last 3 review-or-later).
When timeline or travel matrix are missing, the skill reconstructs them from all chapter drafts. This is the most common use case for discovery writers β the timeline emerges through writing.
/storyforge:continuity-checker my-discovery-book
β No plot/timeline.md found. Reconstructing...
β No world/setting.md (travel matrix) found. Reconstructing...
Timeline reconstructed:
Day 1 (Mon, 14.3.): Arne arrives (ch 1)
Day 2 (Tue, 15.3.): Lighthouse exploration (ch 2)
Day 3 (Wed, 16.3.): Silver coin found (ch 3)
...
Travel matrix reconstructed:
Locations found: lighthouse, harbor, village, parsonage, cliff
Travel events captured: 14 across 27 chapters
Inconsistencies: 2
β Saved to plot/timeline.md and world/setting.md
Memoir books carry two additional quality gates that do not exist for fiction. Both are mandatory β the export-engineer refuses to proceed without them.
/storyforge:emotional-truth-prompt interrogates a chapter draft for felt-sense gaps β the moments where the prose is smooth but the experience is missing. Run it after a chapter draft is complete and before /storyforge:chapter-reviewer.
Workflow position: chapter-writer β emotional-truth-prompt β chapter-reviewer
The skill runs a 7-dimension interrogation (ET1βET7) across the draft:
| Dimension | What it checks |
|---|---|
| ET1 Implicit Feeling | Emotional states named abstractly or left entirely blank |
| ET2 Retrospective Vantage | Present-narrator knowledge slipping into scene-level narration |
| ET3 Memory Contradiction | Suspiciously clean memory; no mixed feelings; unacknowledged gaps |
| ET4 Avoidance Hedge | Vague event references; unnamed refusals; transition elisions |
| ET5 Thoroughness Trap | Inventory detail without felt-sense function |
| ET6 Scene/Summary Mode Error | Hinge moments summarized; transitional content dramatized |
| ET7 "I Was Wrong" Rendering | Penance porn; tidy redemption; wrongness told not rendered |
Output: Questions and revision directions β never prose rewrites. Verdicts:
| Verdict | Meaning |
|---|---|
PASS |
Proceed to /storyforge:chapter-reviewer |
DEEPEN |
Address flagged dimensions, then return for a second pass |
REWRITE |
Scene-mode errors or extensive avoidance β structural rework required |
Only memoir books (book_category: memoir). Offering the skill on a fiction book returns an error and suggests chapter-reviewer instead.
/storyforge:memoir-ethics-checker is the mandatory export prerequisite for memoir books. It runs two passes:
Pass 1 β Consent scan (per person in people/):
| Status | Meaning | Export impact |
|---|---|---|
granted |
Written consent obtained | Clear |
pending |
Consent requested, awaiting reply | Warn β delays export |
not-asked |
Consent not yet requested | Warn |
refused |
Consent explicitly denied | BLOCK |
not-required |
Public figure / already public | Clear |
Pass 2 β Defamation-risk prose scan (four pattern categories):
| Pattern | Category |
|---|---|
| D1 | False-fact assertions about private individuals |
| D2 | Sexual content without consent |
| D3 | Malice markers (reckless disregard, deliberate harm framing) |
| D4 | Financial crime assertions without evidence |
Verdicts:
| Verdict | Condition |
|---|---|
EXPORT CLEAR |
All consents resolved, no D1βD4 patterns |
RESOLVE BEFORE EXPORT |
Pending consents or soft D-patterns |
EXPORT BLOCKED |
Any refused consent or hard D1/D3 pattern |
Workflow position: sensitivity-reader β memoir-ethics-checker β voice-checker β export-engineer
Manual at the start of a chapter / scene:
1. /storyforge:chapter-writer
β
MCP get_chapter_writing_brief(book_slug, chapter_slug)
β bundles 14 data sources into one JSON (incl. pov_character_inventory + pov_character_state)
β
For combat/travel outlines:
MCP verify_tactical_setup(book_slug, scene_outline_text, characters_present)
β flags walking-order issues
Automatic on every Write/Edit on draft.md:
Write/Edit/MultiEdit
β
PostToolUse hook (validate_chapter.py)
ββ Book CLAUDE.md banlist β block on hit
ββ Author vocabulary β block on hit
ββ Meta-narrative β block on hit
ββ Story-time anchor β warn with implied date
ββ Global AI tells β warn
ββ Sentence variance β warn
ββ POV boundary β warn with domain + remediation options
No manual action needed β the hook runs with every write.
1. /storyforge:chapter-reviewer β MCP get_review_brief() loads project-state, then 28-point chapter check + sub-point 20a POV boundary
2. Apply craft fixes (hook checks each fix)
3. /storyforge:chapter-humanizer β AI-construction scan: 8 Section 11 shapes + 55 vocabulary hits; interactive fixes in author's voice
4. Apply humanizer fixes
5. /storyforge:chapter-proofreader β spelling, grammar, punctuation (explanations in the author's native_language)
6. (optional) /storyforge:voice-checker for critical chapters
1. /storyforge:continuity-checker β MCP get_continuity_brief() loads full book state, then Timeline/Travel/Canon validation
2. MCP validate_timeline_consistency β Cross-chapter drift detection
3. /storyforge:manuscript-checker --interactive β Cross-chapter cleanup
4. (optional) /storyforge:voice-checker β Holistic AI-authenticity score
1. /storyforge:sensitivity-reader β Optional, for sensitive topics
2. (Memoir only) /storyforge:memoir-ethics-checker β Consent + defamation gate (mandatory for memoir)
3. (optional) /storyforge:voice-checker my-book β Full-book holistic score
4. All chapters at status `Final`
5. /storyforge:export-engineer
Older books have rules in the CLAUDE.md in the old quote format ("clocked" instead of `clocked`). The hook would not recognize them. Migration via script:
# Dry-run (shows diff, does not write)
~/.storyforge/venv/bin/python3 -m tools.claudemd.migrate_to_backticks \
/path/to/book
# Apply with interactive confirmation
~/.storyforge/venv/bin/python3 -m tools.claudemd.migrate_to_backticks \
/path/to/book --apply
More details and caveats: StoryForge main page β Migration.
Optional schema migration: tactical: and knowledge: blocks can be retroactively appended to character frontmatter. Both are optional β missing blocks lead to graceful degrade. Not every character needs tactical data; only action-relevant ones. Knowledge data only relevant for POV characters.
Symptoms: Write fails with [BLOCK] ..., but the phrase is part of the intended voice.
Causes:
CLAUDE.md as backtick pattern### Absolutely Forbidden sectionFixes:
### Absolutely Forbiddenlinter_mode: warn in book CLAUDE.md frontmatterSymptoms: Hook flags domain vocab in narrator text as POV boundary violation, but the character plausibly knows it.
Causes:
none/layperson but should be competentreference/craft/knowledge-domains/ filenameFixes:
forensics from none to competent)knowledge: block entirely β scan is skipped completely for that characterreference/craft/knowledge-domains/{custom}.md β community-extensiblePossible causes:
vocabulary.md has no banned_wordsFix: Strengthen author profile β see Author Profiles.
Normal for:
Approach: Focus on high-severity findings. Ignore medium-severity in the first pass.
Symptoms: Hook produces a warn message with implied date for every yesterday/tomorrow/an hour ago.
This is by design. The warning is a verification checklist, not a block. If the implied date is correct β ignore. If the warnings get too noisy, you cannot disable them selectively β the entire linter set runs in linter_mode: warn or not at all.
Example: Chapter says "three days later", timeline says "two days later".
Check: Was the day start intra-day (early morning = same day)? Timeline entries must be precise.
Fix:
/storyforge:register-callback Regel: Timeline days are calendar days, not 24h blocks
This persists the semantics in the per-book CLAUDE.md.