Architecture
A 60-second map of how the pieces fit. The design goal throughout: the brain on disk is the checkpoint, so runs are resumable without a graph framework or a running server.
System overview
Section titled “System overview”Interfaces shell/ (interactive TUI — Rich + prompt_toolkit; branding · help · commands · dashboard · chat · dispatch · slash · session · repl) cli/ (one-shot CLI / python writingagent.py — a package) webui/ (local web dashboard — stdlib ThreadingHTTPServer + SSE; 127.0.0.1 only, no auth, one job at a time) │Orchestrator orchestrator/ — durable on-disk state machine (common · book · article · export · manage · review) chapters/sections → consolidate → production → learn (prefetches unit n+1's inputs · parallel commit batch) agentic/ opt-in self-directing controller + learned trace policy (plan §21) │LLM layer nodes.py planner · writer · critic · judge · verifier · summarizer extractor · humanizer · researcher · learner · diagram craft/voice register · field · persona · emotion via the compositor (plan §22–23) llm.py retry/backoff · JSON-repair retry · token telemetry seo.py / promote.py SEO keyword pack + on-page audit · promo/restyle variants OpenRouter DeepSeek V4 Pro / Flash, per-node routing (models.yaml) │Research (opt-in) search.py DuckDuckGo snippets deep_research.py multi-query fan-out + full-page fetch (Scrapo / stdlib) images.py Wikimedia Commons images diagram.py deterministic SVG layout (built-in) + optional D2 / ELK cache.py 7-day disk cache │The brain on disk brain/ markdown chapters · summaries · canon · skills · prefs run_state.json atomic writes + resume guard store.py SQLite FTS5 index + entity graph │Export export.py PDF · EPUB · DOCX · HTML · TXT · MDEvery step persists to the brain before the state machine advances. The unit chain is sequential (each unit reads the previous summary for continuity); everything independent of prose - research, images, skills, and the humanize/summarize/extract commit batch - runs concurrently.
Modules
Section titled “Modules”| Module | Responsibility |
|---|---|
orchestrator/ | The durable on-disk state machine - the brain is the checkpoint. A package behind a stable orchestrator/__init__.py facade (submodules: common · book · article · export · manage · review); orchestrator.X imports are unchanged |
nodes.py | All LLM node functions (planner, writer, critic, humanizer, …) |
prompts.py | Every system prompt, including the wrap_untrusted injection fence |
schemas.py | Pydantic structured-output schemas (validated with a repair retry) |
llm.py | OpenRouter wrapper: retry/backoff, timeout, repair, token budget, cost tallies |
telemetry.py | Per-call JSONL records with per-node + per-unit cost attribution + the /dashboard aggregation |
seo.py | Deterministic on-page audit + a one-call flash keyword pack → seo_report.md (local artifact only - never posts) |
promote.py | Platform-native repurposing (x-thread, LinkedIn, …), voice restyle, and headline variants (local artifacts only) |
brain.py / store.py | Markdown filesystem layout + SQLite/FTS canon & entity graph |
search.py / deep_research.py / images.py / cache.py | The optional research stack |
registers.py / craft.py / exemplars.py / fields.py / gold/*.md | The craft engine - genre register profiles, deterministic craft metrics, few-shot exemplars, structural templates, and the gold style corpus (plan §22) |
compositor.py / personas.py (+ personas/*.md) / emotions.py | The voice cascade (register ⊃ field ⊃ persona ⊃ emotion ⊃ skills): selects one voice + resolves conflicts; 46 personas, 12 anti-cliché emotions (plan §23) |
agentic/ | The opt-in self-directing controller - policies (default · llm · trace), an action trace, in-generation tools, and the learned policy (plan §21) |
shell/ / cli/ / ui.py | Rich TUI, one-shot CLI (both packages), and the 11-theme registry. shell/ is a package behind a stable shell/__init__.py facade (submodules: branding · help · commands · dashboard · chat · dispatch · slash · session · repl); shell.X imports are unchanged |
webui/ | The local web dashboard (writing-agent web): a stdlib ThreadingHTTPServer + Server-Sent Events streaming a single-page app over the same engine - 127.0.0.1 only, no auth, one background job at a time |
export.py | PDF · EPUB · DOCX · HTML · TXT · MD renderers (HTML sanitized) |
Layout on disk
Section titled “Layout on disk”brain/users/<user>/ profile.md prefs/ skills/ books/<book-id>/ plan.json book_plan.md toc.json run_state.json manuscript.md chapters/ eval/ reviews/ instructions/ canon/characters/ world_rules.md timeline.md frontmatter/ backmatter/ consolidation/ articles/<article-id>/ outline.json run_state.json manuscript.md sources.json images/ ← SVG diagrams
config/ models.yaml ← per-agent model routing settings.yaml ← all tunable knobs
.index/telemetry/ ← per-call JSONL records (local only)seeds/skills/ ← built-in craft skillsDesign decisions
Section titled “Design decisions”| Decision | What we chose | Why |
|---|---|---|
| Orchestration | Durable on-disk state machine | Brain on disk = checkpoint; resumable runs without a graph framework |
| Nodes | Deterministic single-purpose LLM calls | The fixed pipeline’s nodes are deterministic and the orchestrator decides the sequence; the opt-in agentic controller (plan §21) layers self-direction on top, with the fixed pipeline as its fallback |
| Skill retrieval | Lexical (BM25) by default | No hard dependency on embeddings; clean seam to enable use_embeddings |
| Cost | Budget cost mode by default | Pins the spend-heavy knobs lean, routes judgment nodes (critic/judge/verifier/consolidation/diagram) to the cheaper flash tier, and applies a per-unit token budget - targeting ≤100k tokens/article with standard available when quality outranks spend |
| Prompt caching | No context compression | Context-compression “headroom” was removed - it perturbed the cacheable prompt prefix and hurt the DeepSeek prompt-cache hit rate for ~no payoff on single-turn payloads |
| Diagram layout | Model authors a spec; Python lays it out (built-in), or D2 + ELK when installed | No overflow/overlap by construction; the built-in engine keeps it zero-dependency |
| Figures | Deterministic reconciliation | Generated diagrams are embedded into the manuscript deterministically; unused images and dropped items go to rejected.jsonl for review (the web Rejected tab), not silently discarded |
| Cross-platform | pathlib, atomic os.replace, no required shelling out | CI runs the full suite on Linux · macOS · Windows, Python 3.10–3.13 |
| Module size | orchestrator/ and shell/ are packages behind stable facades | A behavior-preserving split (preceded by a book↔article de-duplication of shared draft/critique/finalize/learner scaffolding); no file now exceeds ~1k lines, and orchestrator.X / shell.X imports are unchanged for callers |
The conceptual flow that runs on top of this is in How it works.
Contributors should start with plan.md
(the spec) and the Contributing guide.