15 KiB
Story Authoring Workflow
Artifex stores approved fiction state in Django/PostgreSQL and unfinished execution state in LangGraph checkpoints. Draft fragments and EPUB files remain ordinary artifacts under the story's configured artifact root.
Workflow
The story_authoring graph runs these stages:
- Build a chapter context snapshot from the approved bible, outline, canon, previous chapter, and optional source revision.
- Use Terra to generate a self-contained scene plan with exact values, forbidden events, and an explicit ending, then pause for human approval.
- Draft the complete chapter in one resumable Terra call from the approved consolidated beat plan and full previous-chapter canon.
- Use one Luna call to extract the complete chapter state and immutable state changes while checking every required beat, exact value, constraint, forbidden event, final image, and objective continuity issue. Deterministic checks independently enforce known temporal-state and exact-payout rules.
- Validate every proposed state change against exact prose evidence. Hard canon, chronology, exact values, forbidden events, and explicitly required beats can block; optional scene texture remains nonblocking.
- If the combined audit finds objective, locally repairable MEDIUM-or-higher defects, let Luna apply one exact patch touching at most five percent of the chapter. LOW and subjective findings are never patched automatically.
- Run one targeted Luna verifier over only the supplied findings, hard requirements, and changed passages. It cannot discover new issues or trigger another patch.
- Pause for final chapter approval with state coverage, patch metadata, and residual findings attached.
- Atomically commit validated state changes, materialize a cumulative canon snapshot, promote the revision, and rebuild the EPUB.
The approval inbox at /approvals/ resumes the persisted checkpoint. A final request for revision ends
the automated run; it never starts another model loop. Start a new explicit run for additional work.
Production Setup
Story workflows require PostgreSQL for durable cross-process resume. SQLite uses an in-memory checkpoint saver and is suitable only for tests that start and resume in one process.
pip install -e .
export DATABASE_URL=postgresql://artifex:artifex@localhost:5432/artifex
python manage.py migrate
python manage.py seed_spark_resources
python manage.py runserver
Run Artifex on Spark for the simplest deployment. If the Django process runs elsewhere, set ARTIFEX_OPENCODE_TRANSPORT=ssh and ARTIFEX_SPARK_SSH_ALIAS=spark; if it runs on Spark, leave the transport as local. Configure the Terra and Luna OpenCode commands with ARTIFEX_<MODEL>_OPENCODE_COMMAND when they differ from opencode run.
Import And Run
Import an approved story bible and outline, creating locked canon through Chapter 1:
python manage.py story_workflow import \
--slug the-fortune-below \
--title "The Fortune Below" \
--series "Labyrinth Hero" \
--brief /path/to/labyrinth-hero-brief.md \
--plan /path/to/labyrinth-hero-plan.json \
--source-dir /path/to/labyrinth-hero \
--locked-through 1 \
--artifact-root /path/to/artifacts
Start Chapter 2 with the old chapter retained as source material:
python manage.py story_workflow start \
--slug the-fortune-below \
--chapter 2 \
--source /path/to/labyrinth-hero-chapter-02-a-fortune-with-witnesses.md
Start a clean run while retaining but disabling older paused runs:
python manage.py story_workflow start \
--slug the-fortune-below \
--chapter 2 \
--fresh \
--supersede-active
Approve or return a paused gate from the shell:
python manage.py story_workflow resume --graph-run 42 --decision approve
python manage.py story_workflow resume --graph-run 42 --decision request_revision --notes "Slow the transition into the lodging scene."
python manage.py story_workflow resume --graph-run 42 --decision reject
Every model request, context snapshot, review finding, approval, revision, canon snapshot, and publication artifact remains queryable in Django.
State Ledger
StoryEntity gives every tracked person, item, location, account, relationship, organization,
and plot thread a stable key. StateChange stores immutable, evidence-backed transitions for those
entities. ChapterStateDocument stores the readable chapter state and contract coverage, while
CanonSnapshot materializes the complete approved book state for fast generation context.
Only validated changes from an approved revision are committed. Rejected revisions retain proposed changes for audit but never alter canon. Corrections use superseding changes rather than rewriting history.
Build or inspect a state document and query committed entity history:
python manage.py story_state build --revision <revision-uuid>
python manage.py story_state show --revision <revision-uuid>
python manage.py story_state history --slug the-fortune-below --entity character.corin.vale
Each build writes chapter-NN-rN.state.json and chapter-NN-rN.state.md under the story artifact
root. The database remains authoritative; these files are human-readable projections.
Book Authoring State
BookStateVersion is the versioned planning contract for a whole work. Its JSON content owns act and chapter
structure, chapter dependencies, book constraints and forbidden events, arc and thread progression, and planning
continuity facts. Validation checks that structure and dependency graph; separate structural, continuity, and
editorial reviews remain attached to the immutable version. Revisions point to their parent and carry an impact
report so changed chapters and downstream dependencies can be inspected before approval.
Create and operate on book state with fiction_book:
python manage.py fiction_book create --series-slug labyrinth-hero \
--work-slug the-fortune-below --input /path/to/book-state.json
python manage.py fiction_book validate --id <state-uuid>
python manage.py fiction_book review --id <state-uuid> --level continuity --model luna
python manage.py fiction_book impact --id <state-uuid>
python manage.py fiction_book approve --id <state-uuid> --actor daniel
python manage.py fiction_book start-run --id <state-uuid>
python manage.py fiction_book sync-run --run-id <run-uuid>
python manage.py fiction_book review-run --run-id <run-uuid> --model luna
The equivalent API is GET|POST /api/authoring/book-states/, GET /api/authoring/book-states/<uuid>/, and POST /api/authoring/book-states/<uuid>/actions/. Actions are
validate, review, approve, reject, revise, impact, start_run, sync_run, and review_run. A BookRun stores a
durable chapter cursor and completed work, allowing an approved plan to resume without inferring progress from
generated prose. Once every bound scene is approved, the run stops in review until a current whole-run continuity
review passes. Bind a standalone scene to one approved chapter with --book-state and --chapter-key, or the
scene-create JSON fields book_state_id and chapter_key.
Completed runs retain the exact reviewed scene manifest. Start a new run before creating later revisions against
the same approved book state.
Book state is planning authority, not publication authority. Approval does not promote any fact into canon, generate or approve chapter prose, or auto-approve a standalone scene; those remain explicit authoring and human approval steps.
Source Registry
Register source files before extracting continuity claims. Registration stores immutable file versions, SHA-256 hashes, explicit authority labels, and line/character-addressable passages. It does not infer authority from directory or filename conventions and does not promote extracted facts into canon.
Use --dry-run first when inspecting an existing corpus:
python manage.py story_sources register \
--root /path/to/manuscripts/the-fortune-below \
--series-slug labyrinth-hero \
--series-title "Labyrinth Hero" \
--work-slug the-fortune-below \
--work-title "The Fortune Below" \
--authority provisional \
--document-type other \
--dry-run
Authority values are canon, provisional, planning, superseded, rejected, and
noncanon_experiment. Register mixed-authority corpora in separate, explicitly classified batches.
Changing an authority label creates a superseding source version even when the file content is unchanged.
Use repeated --include-glob values to register explicit authority batches while retaining paths relative to
the full corpus root. Shared canon belongs to a series_reference work so every book in that series can retrieve
it without seeing sibling-book drafts.
Standalone Scenes
Standalone scenes have their own persistent plan, cited context pack, frozen requirements, resumable prose,
review, hashes, artifacts, and approval state. They do not require fake chapters and do not alter series canon.
Final approval registers the generated scene as provisional; canon promotion remains a separate decision.
Ask Sol to propose new evidence-backed scenes before creating any scene record:
python manage.py fiction_ideas propose \
--series-slug labyrinth-hero \
--work-slug the-fortune-below \
--target-book "Book Two" \
--focus "Unspent Sabine and Corin choices that preserve current physical continuity" \
--candidate-count 10 \
--include-authority canon \
--include-authority planning \
--model sol
Each persisted proposal includes its exact context hash, authority-labelled citations, candidate briefs, the opportunity each scene spends, future opportunities its ending creates, continuity questions, risks, and prompt/response hashes. The default ten-candidate menu covers ten generic dramatic functions: quiet connection, major turn, physical escalation, conflict pressure, boundary choice, revelation/discovery, aftermath/consequence, competence/task, external plot action, and ensemble/social. These types classify dramatic function without assuming a particular book, cast, genre, or relationship. Proposal is read-only and creates no scene. Inspect a proposal and explicitly select one candidate with:
python manage.py fiction_ideas show --id <idea-uuid>
python manage.py fiction_ideas export --id <idea-uuid> --output /path/to/scene-ideas.md
python manage.py fiction_ideas select --id <idea-uuid> --candidate-id idea-02
Pass --book-state <approved-state-uuid> to freeze the approved book contract into ideation context. Selecting
from a bound proposal also requires --chapter-key, and the resulting scene is bound to that exact state/chapter.
Export renders the persisted proposal, frozen citation index, selection state, and generation hashes as
deterministic Markdown. It does not call a model or mutate the proposal. Selection is idempotent and creates one
planning scene for the existing plan/write/review workflow. Repeating
the same selection returns the same scene. Ideation consults canon and planning by default; pass explicit
--include-authority values to narrow or deliberately expand that evidence set.
--target-book is mandatory for new proposals. The selected book is a hard premise boundary: later-book canon
may constrain consequences, but events, roles, locations, relationships, and abilities first established later
cannot stage the proposed scene. Every candidate exports its prerequisites and explicit book-scope justification.
Each candidate also explains why its primary scene type fits. A physical_escalation must cross or sharply
approach a meaningful established physical threshold; routine care, medical assistance, incidental contact,
bathing, or help dressing and undressing is not sufficient.
Use repeated --scene-type values to request alternatives within one or more dramatic functions. When one type
is requested, multiple candidates may use that type; when several are requested, the proposal covers every
requested type before repeating one:
python manage.py fiction_ideas propose \
--series-slug labyrinth-hero \
--work-slug the-fortune-below \
--target-book "Book Two" \
--candidate-count 3 \
--scene-type physical_escalation \
--model sol
Use --governing-document for an authoritative guide that the model must receive in full. Governing documents
are frozen verbatim into the context pack before supplementary RAG passages are selected. --pin-document
continues to prioritize relevant excerpts and should not be used when complete-document context is required.
Pass --compact to generate lightweight ideation candidates containing only the title, brief, citations,
opportunity spent, and future opportunities. The same flag on fiction_ideas export produces a review Markdown
without planning constraints, risks, citation index, or other full-detail sections.
Create and plan a scene:
python manage.py fiction_scene run \
--series-slug labyrinth-hero \
--work-slug the-fortune-below \
--title "Fourteen Seconds" \
--brief /path/to/scene-brief.md \
--target-words 1800 \
--constraint "Sabine owns the timing." \
--boundary "Stop when Sabine leaves the office."
After fiction_scene create, preview the exact source passages before planning with:
python manage.py fiction_scene context --id <scene-uuid> \
--include-authority canon \
--pin-document path/relative/to/the/registered/corpus.md
The command stops at plan review. Inspect the returned plan and continue explicitly:
python manage.py fiction_scene approve-plan --id <scene-uuid>
python manage.py fiction_scene write --id <scene-uuid>
python manage.py fiction_scene review --id <scene-uuid>
python manage.py fiction_scene approve --id <scene-uuid> --actor daniel
For an intentional plan-to-draft run that still stops before final prose approval:
python manage.py fiction_scene run ... --auto-approve-plan
Context retrieval uses only the latest versions with canon authority by default. Add an authority only when
the scene should deliberately consult it, for example --include-authority planning. Pin a known document with
--pin-document book-two/planning/fortune-below-sabine-corin-turn-and-household-rules.md.
The same workflow is available through JSON endpoints:
GET|POST /api/authoring/ideas/GET /api/authoring/ideas/<uuid>/POST /api/authoring/ideas/<uuid>/actions/with actionselectGET|POST /api/authoring/scenes/GET /api/authoring/scenes/<uuid>/POST /api/authoring/scenes/<uuid>/actions/
Supported actions are context, plan, approve_plan, write, review, approve, and reject. Scene detail
omits prose by default; request ?include_prose=1 only when the caller needs the full draft.
Model Policy
The default book policy uses only Terra, Luna, and Qwen:
- Terra: chapter planning and complete chapter prose.
- Luna: combined continuity/state extraction and objective audit, one bounded exact patch, and targeted verification.
- Qwen3.8 no-thinking: reserved for local structured fallback work.
- Human: plan approval and final chapter approval.
The combined Luna and deterministic audit must reject chronology drift, incorrect economics, canon conflicts, omitted required beats, and prose that continues beyond the approved final image. Automation performs at most one bounded patch; unresolved blockers return to the human gate.