This feature closes both gaps: explicit user control, and a voice that survives the full chain.
21 bespoke genre voicings (draft)
Adventure — Prose: propulsive and forward-leaning; favor strong active verbs and momentum; keep description tied to motion and stakes. Emotional register: characters meet fear with resolve; bravado masks doubt; camaraderie shows through action under pressure. Sensory: distance, terrain, weather, and the body's exertion — heat, thirst, the burn of effort.
Contemporary Fiction — Prose: grounded, observational, unshowy; natural rhythms and present-day texture; let small moments carry weight. Emotional register: restrained, recognizable interiority; people half-say what they mean; emotion surfaces in ordinary friction. Sensory: the everyday — phones, traffic, food, the specific textures of modern life.
Crime — Prose: lean and procedural with grit; concrete nouns, little ornament; let cause and consequence drive the line. Emotional register: moral ambiguity; characters rationalize; loyalty and betrayal sit close together. Sensory: streets, interiors, the worn detail of evidence and place; money, locks, blood.
Dystopian — Prose: controlled and slightly eroded; institutional diction bleeding into private thought; clarity with an undertow of dread. Emotional register: suppressed feeling; hope is dangerous and rationed; defiance flickers and hides. Sensory: surveillance, scarcity, the texture of decay against enforced order.
Fantasy — Prose: immersive and a touch mythic; let worldbuilding breathe through specific, lived-in detail; cadence with grandeur but earned, not purple. Emotional register: high stakes felt personally; wonder and loss intertwined; loyalty and destiny weigh on choices. Sensory: light, landscape, the materials of a world — stone, steel, magic's texture.
Gothic Fiction — Prose: brooding and atmospheric; long sinuous sentences; the setting breathes and the past intrudes. Emotional register: characters haunted by guilt and memory; dread accumulates by suggestion; beauty and horror share a sentence. Sensory: cold, damp, failing light, the weight of old stone and silence.
Historical Fiction — Prose: textured and measured; period-accurate diction without pastiche; detail anchors the reader in time. Emotional register: feeling shaped by the era's constraints; restraint and propriety against private longing. Sensory: craft, dress, food, and labor of the period; the specific dirt and grandeur of the age.
Horror — Prose: escalating dread; controlled pacing that tightens; plain sentences that turn wrong. Emotional register: fear as physical and psychological; denial gives way to terror; the safe becomes threatening. Sensory: sound, shadow, the body's responses — cold sweat, held breath, the wrongness just out of sight.
Literary Fiction — Prose: precise and layered; the exact word over the approximate; subtext over exposition; each paragraph rewards rereading. Emotional register: interiority betrayed by habit and gesture; insight arrives obliquely. Sensory: taste and smell as memory; place with personality; the charged ordinary.
Magical Realism — Prose: matter-of-fact about the impossible; lyrical but unastonished; the marvelous reported as ordinary. Emotional register: wonder folded into daily grief and joy; the surreal carries emotional truth. Sensory: domestic and natural detail rendered vivid; the uncanny grounded in the tangible.
Mystery — Prose: clean and clue-laden; controlled disclosure; every detail load-bearing without telegraphing. Emotional register: curiosity and unease; characters withhold; trust is provisional. Sensory: rooms, objects, and timing — the precise arrangement of who, where, and when.
Noir — Prose: terse and fatalistic; hard-boiled rhythm; dry observation cut with sudden lyricism. Emotional register: cynicism over buried tenderness; everyone is compromised; doom hums under every choice. Sensory: rain, neon, smoke, shadow; the cheap and the gaudy in sharp relief.
Paranormal — Prose: charged and intimate; the uncanny close and personal; sensory immediacy with a current of the otherworldly. Emotional register: desire and dread entangled; characters drawn toward what should frighten them. Sensory: temperature shifts, skin, the electric edge between the seen and unseen.
Romance — Prose: emotionally rich interiority; attentive to gesture, glance, and the charged pause; warmth without saccharine. Emotional register: yearning, vulnerability, the risk of being known; conflict between fear and want. Sensory: touch, proximity, scent, and the heightened detail of the beloved's presence.
Satire Humor — Prose: dry and ironic; deadpan delivery; humor in the gap between what's said and meant; controlled exaggeration. Emotional register: characters deflect with wit; the funniest beats are the saddest; absurdity observed, not announced. Sensory: the mundane and specific rendered with deliberate anti-climax.
Science Fiction — Prose: precise and conceptually clear; cool, exact diction; ideas dramatized through concrete consequence. Emotional register: wonder tempered by rigor; the human inside the systemic; awe and unease at change. Sensory: technology's texture, scale, and the body against unfamiliar environments.
Speculative Fiction — Prose: idea-driven and unsettling-familiar; clear lines that tilt the ordinary; the premise felt through lived detail. Emotional register: disquiet beneath normalcy; characters adapting to a changed rule of the world. Sensory: the recognizable made strange; small concrete signs of the altered reality.
Thriller — Prose: kinetic and tight; short propulsive sentences in action; a ticking-clock pulse. Emotional register: pressure, adrenaline, calculation under threat; trust is a liability. Sensory: proprioception and speed — the body's alarm, the geometry of escape and pursuit.
Urban Fantasy — Prose: gritty-modern with a wry edge; magic braided into city texture; quick, contemporary rhythm. Emotional register: jaded competence over real vulnerability; humor as armor; loyalty hard-won. Sensory: the city at night, the friction of the mundane and the magical sharing a street.
Western — Prose: spare and elemental; plain strong sentences; landscape as force and character. Emotional register: stoic restraint; codes of honor and survival; feeling shown through what's left unsaid. Sensory: dust, sun, distance, leather and iron; the vast indifferent land.
Young Adult — Prose: immediate and voice-forward; close, propulsive narration; contemporary cadence without condescension. Emotional register: emotionally raw and honest; first-time intensity of feeling; identity and belonging at stake. Sensory: vivid, specific, present-tense-feeling detail; the heightened texture of new experience.
Summary
Add a user-editable Author's Voice ("voicing") feature that gives the user explicit control over the prose voice of a generated novel. The feature has three parts:
voicingtextarea on the Step 1 / start page that starts empty and auto-fills with a bespoke per-genre voicing template when the user selects a genre. The user can freely edit it.vocab_rulesalready uses).When the textarea is populated, the user's voicing replaces the auto-selected
voice_seedentirely — the two must not compete.Motivation / current behavior
Today prose voice is handled by
novelforge/voice.py:select_voice_seed(genre, premise)auto-picks one of 8 fixed tonal palettes (weighted by genre + premise keywords + randomness) atnovelforge/routes/outline.py:209, stored insession["voice_seed"].format_voice_prompt()renders it tovoice_prompt(novelforge/routes/generation/chapters.py:280-282) and passes it only to the draft prompt (build_chapter_draft_prompt, called atchapters.py:498-509; param defined atnovelforge/agents/chapter/prompts.py:108-116).Two gaps:
special_instructionsis a free-text catch-all but isn't a dedicated, structured voice mechanism.)voice_promptreaches the draft only. The chapter then goes through ~18 sequential full-rewrite passes in_run_all_chapter_agents(novelforge/agents/chapter/pipeline.py:565) — prose refinement, editing, momentum, structure, synthesizer, polish, anti-LLM, metaphor reduction, quality control, copy edit, etc. Those passes receivevocab_rulesbut not the voice, so each one sands the prose back toward the model's generic default. The distinct voice established at draft is progressively lost.This feature closes both gaps: explicit user control, and a voice that survives the full chain.
Confirmed design decisions
ALLOWED_GENRESentry) — not a reuse of the 8 existing seeds. Drafts provided below.pipeline.py:791), so the existing pure-Python scanner + targeted fix-up runs after it.voicingis non-empty, skip / ignoreselect_voice_seed()so there is exactly one voice source. When empty, fall back to today's auto-seed behavior unchanged.Implementation plan
A. New input field + validation
templates/index.html(genre<select>at line 166-169): add avoicing<textarea name="voicing">to the Step 1 form. Wire a JSchangehandler on#genrethat fills#voicingfrom a genre→voicing map, respecting decision [WIP] Develop Flask web application for generating fiction novels #1 (only fill when empty/untouched — track the last auto-filled value, e.g. via adata-autofilledattribute).<script>block in the template (CSP already allows inline scripts). Render it from the new server-sideGENRE_VOICINGSdata so there is a single source of truth. (Alternative: a small GET endpoint; embedding avoids a round-trip and is preferred.)novelforge/validation.py→validate_outline_input(): add avoicingfield,str, optional, with a max-length cap (suggest 5000 to matchspecial_events/special_instructions). Strip and store invalues["voicing"].B. Persist through session + snapshot
novelforge/routes/outline.py(around209-211): read validatedvoicing; storesession["voicing"]. Whenvoicingis non-empty, do NOT callselect_voice_seed()/ setsession["voice_seed"](decision Harden production defaults: rate limiting backend, config profiles, and deployment readiness #5). When empty, keep current auto-seed behavior.novelforge/session/persistence.py: add"voicing": (str, "")to the schema (nearspecial_instructionsat line 115 /voice_seedat 131); include it in the save dict (near 251/267) and the restore path (near 403/419).novelforge/routes/generation/chapters.pysnapshot (near 140-150, wherevoice_seedis added at thesnapdict): add"voicing": session.get("voicing", "").C. Resolve the effective voice prompt (single source)
chapters.py:280-282: compute the effective voice text:snap["voicing"]is non-empty → use it directly as the voice block (wrap with a small header for consistency, mirroringformat_voice_prompt);format_voice_prompt(voice_seed)as today.voice_promptto the draft (already wired atchapters.py:508) and down into_run_all_chapter_agents(new param — see D).D. Inject everywhere (light touch)
novelforge/agents/chapter/pipeline.py_run_all_chapter_agents(line 565): add avoicing_prompt: str = ""parameter (or carry it onChapterContext— seenovelforge/agents/chapter/context.py:10-26, which is the established pattern for threading chapter-level strings; adding avoicing: str = ""field there is the cleaner option and avoids touching both call sites' positional args)._safe/_build_with_vocab_ruleshelper (pipeline.py:613-624): append a light-touch voicing block to the system message alongsidevocab_rules. Wording: "VOICE TO PRESERVE: … Maintain this voice; do not flatten or normalize it. Do not over-apply it." Keep it short so it doesn't overpower each pass's primary instruction.chapters.py:530andnovelforge/routes/generation/revision.py:193(revision re-runs the pipeline and must loadvoicingfrom the snapshot/session too).E. Dedicated final voicing pass (strong touch)
novelforge/agents/chapter/prompts.py, e.g.build_voicing_pass_prompt(chapter_text, chapter_num, title, voicing_prompt), with a strong-touch instruction: rewrite so the chapter unmistakably reads in the specified voice, while preserving plot, continuity, dialogue meaning, and character actions. Add the corresponding template entry toprompts.yml._run_all_chapter_agents: insert the pass after copy edit and before the terminal vocabulary scan atpipeline.py:791(decision Add full 12-step specialized agent pipeline per chapter #3). Run it via_safe(...)so vocab rules + content-retry apply; the existing scan + conditionalbuild_vocabulary_fix_promptfix-up then cleans up anything the voicing reintroduced. Skip the pass whenvoicing_promptis empty._check_deadline()) andstep_callbacklike every other pass. Cost: +1 LLM call/chapter (~5% of the ~19-call budget).F. Bespoke per-genre voicings (data)
GENRE_VOICINGS: dict[str, str](or structured triplet dict rendered to text) — suggested home:novelforge/voice.py(co-located with the existing seed system) or a newnovelforge/genre_voicings.py. Must cover every entry inALLOWED_GENRES(validation parity check, mirroring the existing_missing_voice_genresguard invoice.py:243-248).21 bespoke genre voicings (draft)
Adventure — Prose: propulsive and forward-leaning; favor strong active verbs and momentum; keep description tied to motion and stakes. Emotional register: characters meet fear with resolve; bravado masks doubt; camaraderie shows through action under pressure. Sensory: distance, terrain, weather, and the body's exertion — heat, thirst, the burn of effort.
Contemporary Fiction — Prose: grounded, observational, unshowy; natural rhythms and present-day texture; let small moments carry weight. Emotional register: restrained, recognizable interiority; people half-say what they mean; emotion surfaces in ordinary friction. Sensory: the everyday — phones, traffic, food, the specific textures of modern life.
Crime — Prose: lean and procedural with grit; concrete nouns, little ornament; let cause and consequence drive the line. Emotional register: moral ambiguity; characters rationalize; loyalty and betrayal sit close together. Sensory: streets, interiors, the worn detail of evidence and place; money, locks, blood.
Dystopian — Prose: controlled and slightly eroded; institutional diction bleeding into private thought; clarity with an undertow of dread. Emotional register: suppressed feeling; hope is dangerous and rationed; defiance flickers and hides. Sensory: surveillance, scarcity, the texture of decay against enforced order.
Fantasy — Prose: immersive and a touch mythic; let worldbuilding breathe through specific, lived-in detail; cadence with grandeur but earned, not purple. Emotional register: high stakes felt personally; wonder and loss intertwined; loyalty and destiny weigh on choices. Sensory: light, landscape, the materials of a world — stone, steel, magic's texture.
Gothic Fiction — Prose: brooding and atmospheric; long sinuous sentences; the setting breathes and the past intrudes. Emotional register: characters haunted by guilt and memory; dread accumulates by suggestion; beauty and horror share a sentence. Sensory: cold, damp, failing light, the weight of old stone and silence.
Historical Fiction — Prose: textured and measured; period-accurate diction without pastiche; detail anchors the reader in time. Emotional register: feeling shaped by the era's constraints; restraint and propriety against private longing. Sensory: craft, dress, food, and labor of the period; the specific dirt and grandeur of the age.
Horror — Prose: escalating dread; controlled pacing that tightens; plain sentences that turn wrong. Emotional register: fear as physical and psychological; denial gives way to terror; the safe becomes threatening. Sensory: sound, shadow, the body's responses — cold sweat, held breath, the wrongness just out of sight.
Literary Fiction — Prose: precise and layered; the exact word over the approximate; subtext over exposition; each paragraph rewards rereading. Emotional register: interiority betrayed by habit and gesture; insight arrives obliquely. Sensory: taste and smell as memory; place with personality; the charged ordinary.
Magical Realism — Prose: matter-of-fact about the impossible; lyrical but unastonished; the marvelous reported as ordinary. Emotional register: wonder folded into daily grief and joy; the surreal carries emotional truth. Sensory: domestic and natural detail rendered vivid; the uncanny grounded in the tangible.
Mystery — Prose: clean and clue-laden; controlled disclosure; every detail load-bearing without telegraphing. Emotional register: curiosity and unease; characters withhold; trust is provisional. Sensory: rooms, objects, and timing — the precise arrangement of who, where, and when.
Noir — Prose: terse and fatalistic; hard-boiled rhythm; dry observation cut with sudden lyricism. Emotional register: cynicism over buried tenderness; everyone is compromised; doom hums under every choice. Sensory: rain, neon, smoke, shadow; the cheap and the gaudy in sharp relief.
Paranormal — Prose: charged and intimate; the uncanny close and personal; sensory immediacy with a current of the otherworldly. Emotional register: desire and dread entangled; characters drawn toward what should frighten them. Sensory: temperature shifts, skin, the electric edge between the seen and unseen.
Romance — Prose: emotionally rich interiority; attentive to gesture, glance, and the charged pause; warmth without saccharine. Emotional register: yearning, vulnerability, the risk of being known; conflict between fear and want. Sensory: touch, proximity, scent, and the heightened detail of the beloved's presence.
Satire Humor — Prose: dry and ironic; deadpan delivery; humor in the gap between what's said and meant; controlled exaggeration. Emotional register: characters deflect with wit; the funniest beats are the saddest; absurdity observed, not announced. Sensory: the mundane and specific rendered with deliberate anti-climax.
Science Fiction — Prose: precise and conceptually clear; cool, exact diction; ideas dramatized through concrete consequence. Emotional register: wonder tempered by rigor; the human inside the systemic; awe and unease at change. Sensory: technology's texture, scale, and the body against unfamiliar environments.
Speculative Fiction — Prose: idea-driven and unsettling-familiar; clear lines that tilt the ordinary; the premise felt through lived detail. Emotional register: disquiet beneath normalcy; characters adapting to a changed rule of the world. Sensory: the recognizable made strange; small concrete signs of the altered reality.
Thriller — Prose: kinetic and tight; short propulsive sentences in action; a ticking-clock pulse. Emotional register: pressure, adrenaline, calculation under threat; trust is a liability. Sensory: proprioception and speed — the body's alarm, the geometry of escape and pursuit.
Urban Fantasy — Prose: gritty-modern with a wry edge; magic braided into city texture; quick, contemporary rhythm. Emotional register: jaded competence over real vulnerability; humor as armor; loyalty hard-won. Sensory: the city at night, the friction of the mundane and the magical sharing a street.
Western — Prose: spare and elemental; plain strong sentences; landscape as force and character. Emotional register: stoic restraint; codes of honor and survival; feeling shown through what's left unsaid. Sensory: dust, sun, distance, leather and iron; the vast indifferent land.
Young Adult — Prose: immediate and voice-forward; close, propulsive narration; contemporary cadence without condescension. Emotional register: emotionally raw and honest; first-time intensity of feeling; identity and belonging at stake. Sensory: vivid, specific, present-tense-feeling detail; the heightened texture of new experience.
Edge cases & guardrails
data-autofilledmarker; only replace when current value is empty or equals the last template inserted./revise_chapter(revision.py:193) must loadvoicingand run identically, including the final pass.ALLOWED_GENRESparity: add a startup validation that every genre has a voicing (mirrorvoice.py:243-248).Testing
validation.py:voicingaccepted, stripped, length-capped (extendtest_app.py/ validation tests).voicingset,select_voice_seedis not used /voice_seednot the voice source; when empty, current behavior preserved.voicing(test_session.py).voicing(test_progress_snapshot.py).test_app.pyprompt-builder tests + a pipeline test withmock_llm).ALLOWED_GENRESentry has a non-empty voicing.Out of scope
Acceptance criteria
voicingtextarea on Step 1; selecting a genre fills it from a bespoke per-genre template; user edits are never clobbered.ALLOWED_GENRES, with a parity check.voicingis set it is the only voice source (auto-seed bypassed); when empty, behavior is unchanged._run_all_chapter_agents.voicingpersists through session save/load/restore, the generation snapshot, and the/revise_chapterpath.mypy novelforge/clean.