product

Reverse Screenplay Engine — architecture (design doc)

Status: Phases 1–4 built; golden-baseline regression deferred. Phase 1 = the adaptation brain (beat sheet) + steering UI. Phase 2 = per-scene Fountain render + downloadable .fountain. Phase 3 = the quality layer (critique panel + apply-and-improve loop + adaptation-fidelity rubric). Phase 4 = FDX export + print-to-PDF HTML + the Scene Studio contract (a rendered scene's Fountain is handed to Scene Studio as its shot-breakdown source via a sourceTextOverride on generateSceneBreakdown, closing story→script→movie). Code: services/screenplay-engine.ts, services/screenplay-critique.ts, services/screenplay-export.ts, routes/screenplay.ts, /dashboard/admin/screenplay, migrations 411_screenplay_engine.sql + 412_screenplay_critique.sql. Deferred: golden-adaptation regression tests — they need a human-approved reference screenplay to diff against (lock one once the first adaptation is approved); and a true server-side PDF (the current path is browser print-to-PDF, no PDF dependency). This doc is the plan for the missing "script" layer in the story→script→movie pipeline. Today Scene Studio jumps straight from chapter prose to a shot breakdown of one strong beat; there is no real screenplay in between. This engine adapts a finished Graphene novella into an industry-standard screenplay that is both a sellable human artifact and the clean scene boundaries Scene Studio consumes. North-star framing: memory project_movie_studio_vision + project_reverse_screenplay_engine.

Thesis: an adaptation engine, not a formatter

A generic prose→screenplay tool has to reverse-engineer everything from raw text — where scenes start and end, what each character wants, where they are, what's subtext vs. surface. Our novella engine already emits all of that as first-class structured data. That is the entire reason this can be good:

What adaptation needs to inferWhat Graphene already has
Scene boundaries (time + place)settings[] in story-bible.ts sensory_palette / rules; chapter_spine ("scene/camera shape guidance")
Beat / act positionstory_episodes.dramatic_role (opening / inciting / rising / midpoint / crisis / climax / resolution)
What a scene must revealstory_episodes.knowledge_layer (what the reader learns vs. what the narrator knows) — the single best driver for adaptation
What's at stake in a scenestory_episodes.chapter_stakes
How a character speaksbible characters[].voice_brief / verbal_tics / speech_avoid / secret_knowledge
Who is speaking which linecharacter-voices.ts detectDialogue / splitProseByVoice
World/canon continuityseason_story_bibles, universe_characters / universe_places

The optimization is: feed the screenplay generator the metadata, not just the prose. Everything below follows from that one move.

Honest constraints (these shape the design)

  • Adaptation ≠ transcription. A 30k-word novella is not a 120-page script. It is a series of decisions: compress, externalize, merge, cut. The engine must make those decisions explicitly and surface them for a human to steer — not silently flatten prose into slug lines.
  • Interiority is the core problem. Novellas live in inner_life and narration. A screenplay can show only what a camera sees and a mic hears. Converting thought into action / behavior / subtext / sparing VO is the hardest pass and gets first-class treatment (below), not an afterthought.
  • Length is a dial, not a constant. A single target_pages knob per project sets the compression budget. Defaults short (~20–30pp) toward the Scene Studio short-film direction; can be turned up toward a feature (~90–120pp).
  • The text layer is cheap; spend the budget on craft. A whole-novella adaptation is sub-$1 in LLM tokens (Fountain is plain text). The expensive spend lives downstream in Scene Studio video. So this layer should run a generous critique loop — quality, not cost, is the gate here.
  • Art-first (Odessa). The screenplay serves the story the writer already lived in. The renderer is downstream; the adaptation should preserve the voice and the throughline, not genericize it into "AI screenplay."

Output format: Fountain canonical, FDX + PDF exports

Fountain (plain-text screenplay markup) is the canonical stored form:

  • It is plain text, so it drops directly into the existing takes → critique → version-snapshot machinery (see episode-takes.ts / episode-take-critiques.ts) — diffs cleanly, snapshots cheaply, no binary blobs.
  • It is free and round-trips to FDX (Final Draft) and PDF for the human / sellable / festival path.
  • Per-scene Fountain bodies map 1:1 to screenplay_scenes rows, which is exactly the granularity Scene Studio wants to consume.

FDX and PDF are exports, generated on demand from the canonical Fountain.

Pipeline — 5 adaptation passes

Novella (chapter_prose × N + full story bible + per-chapter spine/role/stakes/knowledge_layer)
  → 0. Scene re-segmentation   (LLM → screenplay scenes w/ slug lines, grounded in bible settings)
  → 1. Beat sheet + budget     (LLM → scene-by-scene plan against target_pages; dramatize / compress / cut / merge)
  → 2. Interiority externalize  (per scene: inner_life + knowledge_layer → action / behavior / subtext / VO)
  → 3. Dialogue surface + polish (detectDialogue extract; expand summary-dialogue; ground in voice_brief)
  → 4. Fountain render          (assemble scenes + title page → canonical Fountain)
  → FDX / PDF export  +  Scene Studio contract (scenes become canonical shot boundaries)

The passes mirror the proven chapter pipeline (season-novel-chapter.ts prompt-block builder), so the prompt-engineering surface is familiar.

0. Scene re-segmentation (chapters → screenplay scenes)

A screenplay scene = unity of time + place + continuous action. Chapters don't map 1:1 (one chapter is often several scenes; a montage can span chapters). An LLM pass re-segments the novella, emitting for each scene a slug line (INT./EXT. · LOCATION · TIME-OF-DAY) grounded in the bible's settings[], plus source_chapter_ids[] so every scene traces back to its prose. Sonnet planning tier (same tier already used for spine/stakes/bible bootstrap). Output is structured JSON, validated like the existing JSON-envelope chapter calls.

1. Beat sheet + compression budget (the adaptation brain)

Before any screenplay prose is written, decide what to dramatize / compress / cut / merge, against a page budget derived from target_pages (≈1 minute ≈ 1 page; budget allocated by dramatic_role weight). The beat sheet reads dramatic_role + chapter_stakes + planted_threads (with their payoff_target_chapter) so subplots are cut or kept coherently — a planted thread is never dropped without also dropping its payoff. Each beat carries: the scene's job, its knowledge_layer reveal, a page estimate, and a keep/compress/cut/merge decision. This is the human steering point — the sum of page estimates must land near target before generation runs.

2. Interiority externalization (per scene)

The hardest pass. For each scene, convert inner_life / narration / knowledge_layer into something a camera can see: a choice the character makes, a behavior, dialogue subtext, a visual, or — sparingly, flagged, and budgeted — voice-over. The knowledge_layer field tells the pass exactly what the audience must learn in this scene, so externalization is goal-directed rather than decorative. A hard cap on VO density is a critique check (below).

3. Dialogue surfacing + polish

Reuse character-voices.ts detectDialogue to pull dialogue that already exists in the prose verbatim, then expand summary dialogue ("they argued about the money") into real exchanges. Every line is grounded in that character's voice_brief, verbal_tics, and speech_avoid so voices stay distinct and consistent — the same canon the audiobook voice-casting already trusts.

4. Fountain render + assembly

Assemble per-scene Fountain bodies into one document with a title page (logline

  • author + season metadata), correct scene-heading / action / character / parenthetical / dialogue / transition elements, and a final page-count estimate. Canonical Fountain is stored; FDX and PDF are exported on demand.

Quality — the part that actually matters

Because the text layer is cheap, quality is the whole game. Three mechanisms, all reusing infrastructure that already exists for chapters.

A. Screenplay critique panel (reuse the critique loop)

Extend episode-take-critiques.ts with screenplay-specific critics. Take 1 → critics → apply findings → take 2 → promote, exactly like chapters. Proposed critics:

CriticChecks
structure_editorAct breaks land near expected page marks for the target length; midpoint and climax are present and in the right place.
scene_economyEvery scene turns — enters on one value charge, exits on the opposite. Flags inert "as-you-know" scenes.
on_the_nose_doctorDialogue carries subtext; characters don't say exactly what they feel/mean; cuts exposition dumps.
voice_distinctionEach speaker is identifiable from dialogue alone, consistent with voice_brief / verbal_tics; flags interchangeable voices.
vo_disciplineVoice-over stays under budget and is used only where externalization genuinely fails — not as a crutch.
white_spaceFormat density / action-line length / readability — the page looks like a screenplay a reader flies through.
continuity_canonCharacters, places, and planted-thread payoffs stay consistent with the bible / universe canon.

B. Adaptation-fidelity rubric (does it still tell the story?)

A scored evaluator pass (Sonnet) grades the screenplay against the source, not just against screenplay craft, on a fixed rubric: throughline preserved · every planted thread resolved or deliberately cut · each knowledge_layer reveal lands in some scene · protagonist's arc intact · voice/tone faithful to the bible. Low scores route specific scenes back through passes 2–3. This is the guardrail against a technically-correct screenplay that has quietly lost the story.

C. Golden adaptations (regression tests)

Lock one or two reference novellas (e.g. a Turing Logs season) with a human-approved screenplay as the golden output. On any prompt/model change, re-run and diff the beat sheet + rubric scores so a "tuning" change can't silently degrade adaptation quality. Mirrors the spirit of the existing audit patterns (memory feedback_audit_script_pattern).

Non-destructive by default

Every promotion snapshots the prior version (the animatic_versions / keyframe_versions restore pattern), and scenes are individually cached — only edited beats regenerate, so a one-scene fix never re-rolls the whole script.

Data model (proposed — migrations 411+)

Mirror the chapter take/critique tables.

screenplay_projects (
  id uuid pk,
  season_id uuid,             -- the Graphene novella
  target_pages int,           -- the compression dial (e.g. 25 short, 100 feature)
  logline text,
  format text,                -- 'fountain' (canonical); exports tracked below
  status text,                -- draft → beats → scenes → critiqued → final
  fountain_url text null,     -- assembled canonical doc
  fdx_url text null,          -- export
  pdf_url text null,          -- export
  page_estimate int null,
  scene_project_link uuid null, -- contract → scene_studio (optional)
  created_at, updated_at
)
screenplay_scenes (
  id uuid pk,
  screenplay_project_id uuid,
  idx int,                    -- order
  slug_line text,             -- "INT. AUDIT HALL - NIGHT"
  int_ext text,               -- INT | EXT | INT/EXT
  location text,
  time_of_day text,
  beat_role text,             -- from dramatic_role (act/beat position)
  source_chapter_ids uuid[],  -- provenance back to prose
  knowledge_reveal text,      -- what this scene must reveal (from knowledge_layer)
  fountain_body text,         -- the scene in Fountain
  page_estimate numeric,
  vo_lines int,               -- tracked for vo_discipline
  created_at, updated_at
)
screenplay_takes (             -- mirror story_episode_takes
  id uuid pk, screenplay_scene_id uuid,
  fountain_body text, status text,  -- draft → live → archived → rejected
  created_at
)
screenplay_scene_critiques (   -- mirror episode_take_critiques
  id uuid pk, screenplay_take_id uuid,
  critic text, findings jsonb, decision text,
  created_at
)

Beat sheet lives as a column on screenplay_projects (jsonb) or its own screenplay_beats table if the steering UI wants per-beat rows.

LLM tiers (reuse llm.ts)

  • Planning (Sonnet): scene re-segmentation, beat sheet, adaptation-fidelity rubric. Reasoning-heavy, one call each — same tier as bible bootstrap / spine.
  • Generation (Haiku): per-scene Fountain prose. Cheap, parallelizable, cached per scene (~$0.01–0.04/scene).
  • Promote (Opus, optional): re-roll a flagged hero scene at premium quality, same as the chapter promote tier.

Whole-novella adaptation lands well under $1 in tokens. Run the critique loop generously.

UI surface

A Reverse Screenplay admin module (its own page like Scene Studio, or a panel on /seasons/[id]). Per novella: pick target_pages → generate + edit the beat sheet (the steering point) → generate scenes → run critique panel → review fidelity score → assemble → preview Fountain / download FDX + PDF → "send to Scene Studio." Per- scene regeneration and per-critic apply/dismiss are essential (you'll re-roll individual scenes constantly).

Scene Studio contract (the integration win)

screenplay_scenes becomes the canonical, ordered, slug-lined scene list that Scene Studio's generateSceneBreakdown consumes — it stops guessing "the strongest beat" of a chapter and gets real boundaries with location, time, speakers, and dialogue already resolved. One link field (scene_projects.screenplay_scene_idscreenplay_scenes.id) closes the story→script→movie loop end to end.

Phasing

  1. Beat sheet + scene re-segmentation (passes 0–1) + steering UI. Proves the adaptation brain and the target_pages dial. Cheapest, highest-leverage slice; produces a reviewable plan before any screenplay prose exists.
  2. Per-scene generation + Fountain render (passes 2–4). Full canonical screenplay end to end; reuses the chapter prompt-block + caching pattern.
  3. Critique panel + fidelity rubric + golden tests. The quality layer — the reason the engine is worth building. Generous critique because text is cheap.
  4. FDX / PDF export + Scene Studio contract. The sellable artifact and the machine handoff; closes the pipeline.

Risks / open questions

  • Interiority ceiling: how well does externalization preserve a heavily internal novella? Validate pass 2 on one interior-heavy season before committing — this is the make-or-break pass.
  • Compression coherence: cutting a subplot must also cut its setups and payoffs. The planted_threads payoff graph is the lever; verify the beat sheet never orphans a planted thread.
  • Fidelity vs. craft tension: a screenplay can score high on craft while losing the story. The rubric (Quality §B) exists precisely for this; weight it as a gate, not a footnote.
  • Format fidelity on export: Fountain→FDX/PDF edge cases (dual dialogue, montages, intercuts). Pin a parser, golden-test the exports.
  • Voice drift across a long script: distinct character voices over 90+ pages is harder than over one chapter; voice_distinction critic + bible grounding are the mitigations.

Brand alignment

Ships under EmberKiln (graphene.fm) as the script stage of story→script→movie — the same wedge as Scene Studio: the screenplay is born from a story the writer actually lived in and rehearsed (the Odessa fiction→real- change loop), now with a shootable script and a screen at the end of it. Keep the story and the writer's voice primary; the format serves them.

REVERSE SCREENPLAY ENGINE — Docs | HiveJournal