v1.4.0: Organic writing, speed, and log improvements

Organic book quality:
- write_chapter: strip key_events spoilers from character context so the writer
  doesn't know planned future events when writing early chapters
- write_chapter: added next_chapter_hint — seeds anticipation for the next scene
  in the final paragraphs of each chapter for natural story flow
- write_chapter: added DIALOGUE VOICE instruction referencing CHARACTER TRACKING
  speech styles so every character sounds distinctly different
- Lowered SCORE_AUTO_ACCEPT 9→8 to stop over-refining already-professional drafts

Speed improvements:
- check_pacing: reduced from every chapter to every other chapter (~50% fewer calls)
- refine_persona: reduced from every 3 to every 5 chapters (~40% fewer calls)
- Resume summary rebuild: uses first + last-4 chapters instead of all chapters
  to avoid massive prompts when resuming mid-book
- Summary context sent to writer capped at 8000 chars (most-recent events)
- update_tracking text cap lowered 500000→20000 (covers any realistic chapter)

Logging and progress bars:
- Progress bar updates at chapter START, not just after completion
- Chapter banner logged before each write so the log shows which chapter is active
- Word count logged after first draft (e.g. "Draft: 2,341 words (target: ~2200)")
- Word count added to chapter completion TIMING line
- Pacing check now logs "Pacing OK" with reason when no intervention needed
- utils: added log_banner() helper for phase separator lines

UI:
- run_details.html: log lines are now phase-coloured (WRITER=cyan, ARCHITECT=green,
  TIMING=gray, SYSTEM=yellow, TRACKER=purple, RESUME=orange, etc.)
- Status bar shows current active phase (e.g. "Status: Running — WRITER")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 10:59:08 -05:00
parent 958a6d0ea0
commit edabc4d4fa
5 changed files with 105 additions and 29 deletions

View File

@@ -411,7 +411,7 @@ def update_tracking(folder, chapter_num, chapter_text, current_tracking):
{json.dumps(current_tracking)}
NEW_TEXT:
{chapter_text[:500000]}
{chapter_text[:20000]}
OPERATIONS:
1. EVENTS: Append 1-3 key plot points to 'events'.
@@ -594,7 +594,7 @@ def refine_persona(bp, text, folder):
except: pass
return ad
def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None):
def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None, next_chapter_hint=""):
pacing = chap.get('pacing', 'Standard')
est_words = chap.get('estimated_words', 'Flexible')
utils.log("WRITER", f"Drafting Ch {chap['chapter_number']} ({pacing} | ~{est_words} words): {chap['title']}")
@@ -662,6 +662,13 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None):
trunc_content = prev_content[-3000:] if len(prev_content) > 3000 else prev_content
prev_context_block = f"\nPREVIOUS CHAPTER TEXT (For Tone & Continuity):\n{trunc_content}\n"
# Strip future planning notes (key_events) from character context — the writer
# should not know what is *planned* to happen; only name, role, and description.
chars_for_writer = [
{"name": c.get("name"), "role": c.get("role"), "description": c.get("description", "")}
for c in bp.get('characters', [])
]
total_chapters = ls.get('chapters', '?')
prompt = f"""
ROLE: Fiction Writer
@@ -705,7 +712,9 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None):
- CHARACTER INTERACTIONS: If characters are meeting for the first time in the summary, treat them as strangers.
- SENTENCE VARIETY: Avoid repetitive sentence structures (e.g. starting multiple sentences with "He" or "She"). Vary sentence length to create rhythm.
- GENRE CONSISTENCY: Ensure all introductions of characters, places, items, or actions are strictly appropriate for the {genre} genre. Avoid anachronisms or tonal clashes.
- DIALOGUE VOICE: Every character speaks with their own distinct voice (see CHARACTER TRACKING for speech styles). No two characters may sound the same. Vary sentence length, vocabulary, and register per character.
- CHAPTER HOOK: End this chapter with unresolved tension — a decision pending, a threat imminent, or a question unanswered.{f" Seed subtle anticipation for the next scene: '{next_chapter_hint}'." if next_chapter_hint else " Do not neatly resolve all threads."}
QUALITY_CRITERIA:
1. ENGAGEMENT & TENSION: Grip the reader. Ensure conflict/tension in every scene.
2. SCENE EXECUTION: Flesh out the middle. Avoid summarizing key moments.
@@ -724,7 +733,7 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None):
CONTEXT:
- STORY_SO_FAR: {prev_sum}
{prev_context_block}
- CHARACTERS: {json.dumps(bp['characters'])}
- CHARACTERS: {json.dumps(chars_for_writer)}
{char_visuals}
- SCENE_BEATS: {json.dumps(chap['beats'])}
@@ -735,13 +744,15 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None):
resp_draft = ai.model_writer.generate_content(prompt)
utils.log_usage(folder, ai.model_writer.name, resp_draft.usage_metadata)
current_text = resp_draft.text
draft_words = len(current_text.split()) if current_text else 0
utils.log("WRITER", f" -> Draft: {draft_words:,} words (target: ~{est_words})")
except Exception as e:
utils.log("WRITER", f"⚠️ Failed Ch {chap['chapter_number']}: {e}")
return f"## Chapter {chap['chapter_number']} Failed\n\nError: {e}"
# Refinement Loop
max_attempts = 5
SCORE_AUTO_ACCEPT = 9
SCORE_AUTO_ACCEPT = 8 # 8 = professional quality; no marginal gain from extra refinement
SCORE_PASSING = 7
SCORE_REWRITE_THRESHOLD = 6