Markus Michalski FAQ

StoryForge: Technical Details

Technical details of StoryForge: hooks and prose checks, book rules, story-time anchor, chapter writing brief, plot logic, status model, project layout, databases and migration scripts.

StoryForge: Technical Details

Deutsche Version

← Back to overview


Project structure of a book

Standalone books live under {content_root}/projects/{book-slug}/, volumes of a series under {content_root}/series/{series-slug}/{book-slug}/.

{book-slug}/
β”œβ”€β”€ README.md           # Book metadata (YAML frontmatter)
β”œβ”€β”€ CLAUDE.md           # Book rules, callbacks, linter_mode
β”œβ”€β”€ synopsis.md         # Blurb and long synopsis
β”œβ”€β”€ world-rules.md      # fiction only: canonically fragile facts
β”œβ”€β”€ plot/               # outline.md, acts.md, arcs.md, timeline.md, tone.md, canon-log.md
β”œβ”€β”€ characters/         # INDEX.md and one file per character
β”œβ”€β”€ world/              # setting.md (travel matrix), rules.md, history.md, glossary.md
β”œβ”€β”€ research/           # sources.md and notes/
β”œβ”€β”€ chapters/
β”‚   └── {NN-slug}/
β”‚       β”œβ”€β”€ README.md   # Chapter metadata, chapter timeline, promises
β”‚       └── draft.md    # the prose
β”œβ”€β”€ cover/              # brief.md, prompts.md, art/
β”œβ”€β”€ promo/              # created on demand
β”œβ”€β”€ export/             # front-matter.md, back-matter.md, blurb.md, output/
└── translations/       # {language}/ with glossary.md and chapters/

With book_category: memoir, people/ replaces characters/, world/ and world-rules.md are omitted, and plot/ contains structure.md and people-log.md instead of acts.md, arcs.md and canon-log.md.


Status model

Level Order
Book Idea, Concept, Research, Plot Outlined, Characters Created, World Built, Drafting, Revision, Editing, Proofread, Export Ready, Published
Chapter Outline, Draft, Revision, Polished, Final
Character Concept, Profile, Backstory, Arc Defined, Final
Idea raw, explored, developed, ready, shelved, promoted

The book status is derived from the chapters and only ever moves forward:

Book status Trigger
Drafting at least one chapter is past Outline
Revision every chapter is at least at Revision
Proofread every chapter is Final

You set Editing, Export Ready and Published yourself. A higher status you set is never downgraded. rebuild_state() writes the derived status back to the README.

Chapter status aliases are recognised: review and reviewed count as Revision, drafting as Draft, polishing as Polished, done as Final.


Hooks

The plugin registers five hook commands on three events. They run through the wrapper bin/run-server with the virtual environment's Python.

Event Script Effect
PostToolUse (Write, Edit, MultiEdit) hooks/validate_chapter.py Prose check of a draft.md
PostToolUse (Write, Edit, MultiEdit) hooks/validate_character.py Structure check of character and people files
PostToolUse (Write, Edit, MultiEdit) hooks/snapshot_draft.py post Merges Claude's change into the comparison baseline
PreToolUse (Write, Edit, MultiEdit) hooks/snapshot_draft.py pre Captures the draft right before Claude's write
PreCompact hooks/precompact_sync_claudemd.py Saves rules, workflows and callbacks before context compaction

Prose check (validate_chapter)

The hook runs after the write. It only checks files named draft.md below a chapters/ directory, and only when the text has at least 50 words. It locates the book root (the directory containing chapters/ and README.md) and runs these checks:

Check Level Subject
Book rules block Patterns from the book rules and the banned patterns in plot/tone.md that are written in backticks
Author banlist block or warn the author's banned phrases (don'ts) and recurring tics, each with its own level
Meta-narrative block narration that talks about the book's structure, such as Ch 5, callback, as established, echoes the earlier, foreshadow, calls back to, parallels the earlier
Story-time anchor warn relative time phrases such as yesterday or an hour ago against the chapter start
AI tells and global shapes warn patterns from reference/craft/anti-ai-patterns.md (English-language books only)
Sentence-length variance warn standard deviation of sentence length below 4 words with more than 10 sentences
POV boundary warn specialist vocabulary the POV character does not know according to their knowledge profile

In strict mode the hook exits with code 2 on a block-level finding. The message goes back to Claude, which has to correct the text. In warn mode (see configuration) blockers are only printed. Warnings never block.

StoryForge linter flagged this write as blocking:
  [BLOCK] draft.md line 12: Banned phrase from book CLAUDE.md: 'clocked'
Plus 2 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.
The file was already written but is invalid β€” fix the blocking issues. Set `linter_mode: warn` in the book's CLAUDE.md frontmatter to override.

The same rule set is available as the tool validate_chapter.

Character check (validate_character)

The hook acts on .md files under characters/ and people/. INDEX.md is skipped. Also skipped is only the exact path of a series tracker, series/{series}/characters/{file} or series/{series}/people/{file}. Character files of books under series/{series}/{book}/characters/ sit one level deeper and are checked normally.

PreCompact

Before every context compaction the hook reads the transcript, looks in your messages for lines with the prefixes Regel: (also Rule:), Workflow: and Callback: and stores them for the active book (the book from session.last_book in the state cache). No language model is involved, it is plain pattern matching.

Draft snapshots (snapshot_draft)

hooks/snapshot_draft.py keeps two versions of a fiction chapter. It checks nothing, never blocks, prints nothing and swallows errors. Memoir chapters are skipped.


Book rules

A book's rules, workflows and callbacks live in the database and are created through append_book_rule, append_book_workflow, append_book_callback or the skill /storyforge:register-callback.

For the prose hook only what a rule puts in backticks counts:

Notation Effect
`clocked` Hard pattern. If the text contains regex characters it is treated as a regular expression, otherwise as plain text. An exception applies to a nested, unboundedly-quantified group such as (a+)+ (ReDoS risk): it is enforced as plain text instead. Simply quantified groups such as (foo)+ or (?:being){2} remain regex. Case is ignored.
`journey` max 2 per chapter Allows up to N hits per chapter. The limit is scaled by the current draft length relative to the chapter's target word count.

Recognised limit notations: max N per chapter, maximum N per chapter, limit to N per chapter, a range max N-M per chapter (the upper bound counts) and per kapitel.

Use lint_book_rules or /storyforge:rules-audit to check rules for wordings the scanner cannot catch.


Banned patterns from plot/tone.md

The "Banned Prose Patterns" section in a book's plot/tone.md is read by the machinery. The heading may sit at any level from ## to ######; the section runs to the next heading of the same or a shallower level. Bullets with a phrase in backticks are enforceable, and an addition such as max 1 per chapter caps hits per chapter exactly like book rules:

## Banned Prose Patterns

- `suddenly` Max 1 per chapter
- Avoid too many rhetorical questions in a row.

In the example only the first bullet is enforceable. The second carries no backtick phrase and stays a plain hint that nothing scans: reviewers see it under tonal_rules.banned_prose_unenforced, the humanizer warns about it, and the chapter writing brief carries it under tone_banned_patterns.advisory_unenforced.

Enforceable tone rules act like book rules: the prose hook blocks them with a per-chapter cap, the manuscript checker reads them in alongside book rules, and scan_chapter_ai_tells/check_replacement_text honor them. The template templates/plot-tone.md sets the heading at level 2.


Book language and the global catalog

The global catalog (reference/craft/anti-ai-patterns.md, section 1 vocabulary and section 11 banned shapes) applies only to books written in English. The language comes from the language field in the book's README.md YAML frontmatter (default en; en-US counts as en). For any other language a file anti-ai-patterns.<language>.md is expected; none exists at present, so the catalog hits are simply dropped for such books rather than applying English patterns blindly. This affects the hook categories ai_tell and global_shape_violation, the manuscript checker's ai_tell_violation and global_shape_violation, and the humanizer's pre-scan. scan_chapter_ai_tells and check_replacement_text return a warning instead of catalog hits for such books. Book rules, author bans, tics and tone rules still apply regardless of the book's language.


Author banlist

The author's discoveries (banned phrases, recurring tics, don'ts, style principles) live in the database authors.db. Of these, the prose hook only scans the don'ts and the recurring tics. It does not scan style principles. chapter-writer, chapter-reviewer and author-check read them through get_author and the chapter writing brief. The file vocabulary.md is reference only and is not read for enforcement. Add a new banned phrase with write_author_banned_phrase or add_vocabulary_entry. create_author also seeds 19 common AI-tell words (such as delve, tapestry, nuanced, resonate, pivotal) into the database as don'ts on creation, and they take effect immediately.

The hook finds a book's author through the line - **Author:** Name in the Book Facts section of the CLAUDE.md, falling back to the author: field in the YAML frontmatter of the book's README.md.


Story-time anchor

Each chapter can have a ## Chapter Timeline section in its README.md with start and end times of the scenes, for example **Start:** Tue Dec 24 ~19:30. From the start the plugin computes which date relative phrases such as yesterday or tomorrow mean. The prose hook warns on every such phrase with the derived date so you check it against plot/timeline.md. The tool get_current_story_anchor returns the mapping directly:

{
  "available_relative_phrases": {
    "yesterday": "Mon Dec 23 ~19:30",
    "tomorrow": "Wed Dec 25 ~19:30"
  }
}

get_recent_chapter_timelines(book_slug, n) returns the time grids of the last N chapters so that new chapters are anchored to real times instead of remembered ones. validate_timeline_consistency checks time drift across the whole book.


Chapter writing brief

get_chapter_writing_brief(book_slug, chapter_slug) replaces many single read steps in the skill chapter-writer with one JSON containing these parts:

Each component is guarded individually. If one fails, it appears in errors and the brief still arrives with the remaining data. A frequent cause is a missing world/setting.md or plot/timeline.md.

Tactical check

verify_tactical_setup checks combat and travel scenes against the optional tactical block in a character's frontmatter:

tactical:
  protector_role: false
  protected_role: true
  combat_skill: none     # none|low|medium|high|elite|unknown
  movement_lead: false
  movement_rear: false
  vulnerable_to: [daylight, silver]
  carries: [knife]

Rules: a protected character at the rear (R1) or at the lead (R2) of the group warns. A protected character present with no protector positioned warns (R3). A lead position that does not match movement_lead: true warns (R4). Without tactical data there is only a note (R0). The tool also returns questions for the author as a checklist.

POV boundary

The optional knowledge block in a character's frontmatter assigns knowledge domains to the tiers expert, competent, layperson and none:

knowledge:
  expert: [it, networking]
  competent: [photography]
  layperson: [history]
  none: [forensics, medicine]

Specialist vocabulary of a domain that is none or layperson for the POV character produces a warning. Matches are searched as whole words. Free-form domain names count as competent. Without a knowledge block the character is not checked. Bundled vocabulary lists live under reference/craft/knowledge-domains/: forensics, tactical_combat, medicine, ballistics, automotive_repair.


Plot logic and chapter promises

analyze_plot_logic(book_slug, scope?, chapter_slug?) builds a knowledge index from canon facts, timeline and promises and runs two deterministic checks:

Category Check Memoir
causality_inversion A chapter refers to a fact whose chapter lies later in story time active
chekhov_gun A registered promise without payoff (manuscript scope only) skipped

The semantic categories information_leak, motivation_break, premise_violation and pov_knowledge_boundary are judged by the skills chapter-reviewer and manuscript-checker using the knowledge index. premise_violation does not apply to memoir.

Promises are setup elements (Chekhov elements, mysteries, callbacks) per chapter. register_chapter_promises writes them to the ## Promises section of the chapter README, get_chapter_promises reads them. For chapters already written, /storyforge:backfill-promises fills them in. The source of truth remains the canon log in the database (add_canon_fact).


Author evolution

Discoveries move across three levels: book, author, global. /storyforge:harvest-author-rules calls harvest_book_rules and sorts a book's rules into banned_phrase, style_principle and world_rule. For each candidate you decide: promote, keep in the book only, discard, or edit and promote. Promoted entries carry an origin such as _(emerged from firelight, 2026-04)_. If the same discovery shows up in a later book, a second origin is added instead of a duplicate. /storyforge:promote-rule lifts a single rule to author or global level.

The sections in the author profile are Recurring Tics, Style Principles and Don'ts. chapter-writer and chapter-reviewer load them through get_author.


Pre-export checks

run_pre_export_gates checks:

Gate Blocks
All chapters Final yes
At least one chapter exists yes
Word count at least 80% of target no, warning only
Synopsis with more than 50 words no, warning only

With --arc, export-engineer may bypass the "All chapters Final" gate after explicit confirmation. Further rules of the skill:


Databases

Besides the Markdown files, StoryForge uses SQLite databases under ~/.storyforge/db/:

File Content
storyforge.db Session (active book, chapter, phase)
authors.db Author discoveries (author_discoveries) and logged review feedback (review_feedback)
{series-or-book-slug}.db Canon facts, character snapshots, book rules and callbacks, cover tracking

The comparison baselines and raw drafts of fiction chapters under ~/.storyforge/snapshots/ are not a database, just plain files.


Migrating older books

Moving from Markdown data to the databases is a one-time job using scripts in the plugin's scripts/ directory. Run them with the virtual environment's Python, in this order. All are repeatable, entries that already exist are skipped.

Step Script Preview and execution
1. Book rules and callbacks from CLAUDE.md scripts/migrate_book_rules.py --dry-run only shows. Without the flag it writes. --clear-markers then empties the marker sections.
2. Canon log (plot/canon-log.md, people-log.md) scripts/migrate_canon_log_to_db.py Without a flag preview only, --execute writes.
3. Writing discoveries from profile.md and character snapshots scripts/migrate_phase3.py --dry-run only shows. Without the flag it writes.
4. Banned words from vocabulary.md scripts/migrate_vocabulary_to_db.py Without a flag preview only, --execute writes.
5. Backfill source_genres Skill /storyforge:migrate-source-genres for authors who used study-author earlier

For books with old rules in quotation marks instead of backticks there is python -m tools.claudemd.migrate_to_backticks <book-path>. Without --apply it only shows the change, with --apply it asks before writing. Run it from the plugin directory.

To backfill existing books, use the skills /storyforge:backfill-promises and /storyforge:backfill-style-principles.