Skip to content

Changelog

This is a curated summary. The authoritative, full-detail log is CHANGELOG.md in the repo. The format follows Keep a Changelog and the project adheres to Semantic Versioning.

  • Local web dashboard (writing-agent web). A browser UI over the same engine the TUI drives - pure-stdlib ThreadingHTTPServer + Server-Sent Events + a single-page app in src/writingagent/webui/. writing-agent web [--port N] [--no-browser] (default port 8787, auto-opens the browser). Binds 127.0.0.1 only, no auth (same trust boundary as the TUI), and runs one background job at a time. Views: Studio, Live run (SSE log + run controls), Projects, Project (Overview / Activity / Evals / Artifacts / Rejected / Export / Cost), Telemetry, Skills, Settings. Export offers every format (pdf/epub/html/docx/txt/md) plus a Rewrite (restyle) action; theme picker is System / Light / Dark + the named palettes. Follows the editorial design system (design.md).
  • SEO layer (seo.py, writing-agent seo). A deterministic on-page audit (title/meta lengths, keyword placement + density, heading hierarchy, word-count floor, reading grade, outbound-link and image-alt hygiene) plus a one-call flash-tier keyword pack (primary/secondary keywords, meta description, per-platform hashtags). Runs automatically as part of a finished write (gated by auto_promote); the target keyword is threaded into the writer up front (seo_keyword) and the H1 title is optimized after validation. Writes seo_report.md + keywords.json. It only edits the article and names keywords/hashtags - it never posts or schedules anything anywhere. Pin a keyword with writing-agent seo --keyword "…".
  • Promote / repurpose / restyle (promote.py, writing-agent promote). Platform-native variants of a finished piece - x-thread, linkedin, newsletter-teaser, tldr (each one flash call) - plus 5 headline variants and a voice restyle (register / persona / emotion), all reusing the SEO keyword pack. writing-agent promote --to x-thread,linkedin selects formats; runs automatically after a finished write when auto_promote is on. Generates LOCAL promo/*.md artifacts only - nothing is posted or scheduled anywhere.
  • Cost - budget mode (cost_mode). cost_mode: budget (the shipped default in config/settings.yaml) applies a per-unit token budget (budget_tokens_per_unit, default 20000) plus a fixed overhead so a full piece finishes instead of pausing, routes the judgment nodes (critic / judge / verifier / consolidation / diagram) to the cheaper flash tier, and targets ≤100k tokens/article. max_run_tokens remains a hard ceiling that pauses the run when hit and always wins over the auto-scaled budget. Per-node and per-unit telemetry attribution powers the Telemetry / Cost views.
  • Figure reconciliation + Rejected review. Generated diagrams are deterministically embedded into the manuscript; unused images and dropped draft variants are logged and reviewable in the web Rejected tab (rejected.jsonl + versions/).
  • Firecrawl search provider. search_provider: firecrawl (needs FIRECRAWL_API_KEY in .env) as an alternative to the default duckduckgo; a missing key degrades back to DuckDuckGo. verify_excerpt_chars (default 6000) sets the claim verifier’s full-text excerpt size.
  • Editorial design system (design.md v2). A portable, cross-domain design system - ink on warm paper, one accent = manuscript red #a3341f, Fraunces serif for display + reading, the pilcrow brand mark, the Caret loader, flat/borderless, square content surfaces, WCAG-AA verified. Governs both the web dashboard and the TUI (default editorial skin - “ink & brass”: gold primary + brass + manuscript-red status). Named themes recolor, never restructure. Spec lives at the repo root design.md.
  • Write in any field - the craft engine + compositor. A register-parameterized craft engine (11 genre registers, few-shot exemplars, a shipped gold style corpus, and genre-aware deterministic metrics) plus a compositor that layers a manner and an emotion over the register: 46 personas (18 archetypes + 28 public-domain manners - e.g. shakespearean, austen-ironic, wildean, dickensian, whitmanesque, twain-vernacular; no living authors, original-pastiche exemplars) and 12 emotions with anti-cliché deny-lists wired into the detector. The cascade register ⊃ field ⊃ persona ⊃ emotion ⊃ skills selects one voice and logs why, never stacks. New settings: register, field, citation_style, craft_passes, persona, emotion. (plan §22–23.)
  • Self-directing mode - the agentic controller (opt-in, off by default). Instead of the fixed order, a policy chooses each unit’s next move (draft / gather research / read canon) and the writer can call research/canon tools mid-draft; bounded by per-unit step + token caps, with the fixed pipeline as the always-on fallback (off = byte-identical). A policy learned from your own run traces (agentic_policy: trace) improves the choices and abstains until it has ≥3 labelled units per arm. New /agentic and /trace commands surface the mode and the learned verdict; settings agentic, agentic_policy, agentic_controller_model, agentic_max_unit_steps, agentic_inline_tools, … (plan §21.)
  • Learning loop v2 - ablation duels (skill_duels, opt-in). On a unit with an undecided skill, an extra draft is written with that skill held out and the critic compares it to the full-skill draft - a true cause-and-effect test of the skill’s lift. reconcile prefers a smoothed, sample-gated duel win-rate over the first-pass fallback, and /skills shows it. Adds skill_distill (deterministic, non-destructive retirement of near-duplicate skills; off) and watch_blocking (watch-list blocks only clear/concrete violations; false = advisory).
  • learning.md - a layman’s, chronological guided tour of the whole codebase (folders, files, the studio-of-specialists model, the brain-on-disk design, model routing, and the why behind each).
  • Colourblind-safe highcontrast theme (Okabe-Ito; ok = blue, error = vermillion - never a red/green pair). 11 themes total.
  • Whole-run ETA on the live dashboard (~Nm left, from this session’s average time-per-unit).
  • First-run onboarding. With no API key set, the welcome shows how to set the key or try the whole flow free with WRITINGAGENT_FAKE=1 - instead of suggesting a command that would fail.
  • Evidence report (shareable proof). Every article ships an evidence_report.md - the thesis it argues + every source ranked by influence (0–100), built from the finished piece with no model call. Auto-generated at assembly, refreshed by polish, regenerated on demand by the new evidence command (Project.evidence_report()).
  • Clean references, citations & figures (deterministic polish). A pure-Python pass (no LLM, ~0 tokens) builds one end ## References list ranked by influence (rank_references), strips inline [N] markers from the body after scoring (strip_inline_citations), pulls out stray reference dumps, and de-duplicates figures. The new polish command re-applies the whole pass to an already-finished manuscript and re-exports - no model call.
  • TUI UX overhaul. No command dead-ends (a reserved word without its slash runs the command; \ forces chat); a normalized trust chip (✓ approved · insight 5/5 · confidence ●●●○○); live run controls (esc/p pauses cleanly at the next unit boundary, m drops to manual); a structured paused card and recover-from-here export errors; accessibility (WRITINGAGENT_A11Y line-mode, WRITINGAGENT_REDUCED_MOTION) + a narrow-terminal wordmark; a proactive no-key warning; and progressive /help <topic>.
  • Quality machinery II - independence, verification, compounding. A side-by-side tournament judge picks the best of N divergent drafts (replacing scalar self-score selection); claim↔source verification makes unsupported cited claims blocking under deep research; the writer engages the thesis counterargument; a closed table-read loop (table_read_revise) applies the reader’s top fix as one bounded revision; the learner distils skills from the model’s own preference data (tournament + revision outcomes), candidate-only. New judge/verifier model slugs and tournament_judge / verify_claims / table_read_revise settings, surfaced in /model, /features, and the chat context.
  • Diagrams rebuilt - structured spec → deterministic layout. The model now authors a DiagramSpec; a layout engine measures text and places everything on a grid, so labels never overflow or collide (back edges are detected so a feedback arrow can’t reverse a pipeline; arrowheads are explicit, PDF-safe). An optional D2 + ELK backend (diagram_engine) handles complex graphs, with an injected legend; the zero-dependency built-in engine is the default and fallback.
  • writingagent npm launcher - a zero-dependency, globally installable Node CLI (npm install -g writingagent) that forwards to the Python engine. A writingagent setup bootstrap installs the whole stack best-effort (engine via pip from GitHub, cairosvg, the d2 binary) - and the first run offers to do it for you; writingagent update pulls the latest, writingagent doctor shows how the engine was resolved.
  • Shipped defaults flipped to match common usage - new projects default to mode: article, autonomous: true (never pause for review), and use_images: true.
  • Public Python API - a stable Agent + Project facade (create, run, resume, revise, evaluate, export) plus a one-shot write(), re-exported from the package root.
  • Quality machinery (originality over slop-absence) - a per-article thesis (contestable claim + steelmanned counterargument) injected and enforced by the critic; voice exemplars (voice/, fed by /praise); divergent first drafts (best-of-N at varied temperatures); an insight score (1–5) with a min_insight gate plus clarity/structure/evidence scores and deterministic style metrics; a surgical humanizer that rewrites only flagged sentences.
  • Trust machinery - version snapshots (<project>/versions/) with versions and read --v K; revise --chapter N to rewrite one committed unit with a semantic + text diff; brief (goal panel); tableread [--as "persona"]; eval (judged 5-dimension rubric + deterministic metrics → eval_report.md).
  • Interactive TUI - escalation picker on a stalled unit, manual divergent-variant picking, an outline+thesis approval gate after new, a post-run summary card + terminal bell, and a draft-opening glimpse in the dashboard.
  • write command - one-shot autonomous flow: an upfront LLM-generated interview, then a fully autonomous run to a finished, exported file.
  • Theme system - 10 TUI themes, each with its own palette, wordmark figlet face, and glyphs; switch live with /theme. Accent-distinctness and font availability are test-enforced.
  • Deep multi-source researcher (deep_research) - query expansion, concurrent search, full page-text fetch (Scrapo or stdlib), cross-source synthesis with numbered citations.
  • Export overhaul - PDF code/diagram wrapping, Mermaid → PNG via mermaid.ink (cached), EPUB image items, glyph normalization, placeholder cleanup, reference renumbering.
  • Run cost kill-switch (max_run_tokens), structured telemetry (per-call JSONL + /dashboard), prompt-injection defense (data-only fencing), and a fetch safety gate (SSRF guard + robots.txt + per-host rate-limiting).
  • Animated run dashboard, compact welcome screen + /features, run-mode toggle (/auto, run --autonomous/--manual), autocomplete + persistent history, and a --plain / NO_COLOR mode.
  • Open-source scaffolding: MIT LICENSE, CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md, issue/PR templates, GitHub Actions CI (Linux/macOS/Windows × Python 3.10–3.13), ruff, and pre-commit hooks.
  • Internals reorganised into packages (behavior-preserving). The two largest modules were split behind stable facades, so every orchestrator.X / shell.X import is unchanged: orchestrator/ (common · book · article · export · manage · review) and shell/ (branding · help · commands · dashboard · chat · dispatch · slash · session · repl). Preceded by a book↔article de-duplication pass; no file now exceeds ~1k lines.
  • Friendlier, recoverable errors - bad/missing API key, rate-limit, network blip, and locked export files now show a clear next step (ui.explain_error) instead of a raw RuntimeError: ….
  • /features lists the new toggles (skill_duels, skill_distill, watch_blocking); live-run controls wording clarified - all interrupts are resumable (/delete discards).
  • Diagrams: structured spec → deterministic renderer. The model no longer emits SVG (it can’t measure text, so labels overflowed); it now returns a structured DiagramSpec and a pure-Python layout engine (diagram.py) measures text, sizes boxes to fit, places nodes on a grid, routes orthogonal edges, and draws explicit, PDF-safe arrowheads (back edges detected so a feedback arrow can’t reverse a pipeline).
  • Token / cost-efficiency pass (telemetry-grounded, quality unchanged): cache-hit telemetry in the usage summary + JSONL; a lossless JSON-Schema shrink on structured calls; a thesis brief (claim+arguments only) to the critic/judge; per-node max_tokens; and an opt-in divergent_skeletons (draft variants short, expand only the winner - ~60% fewer discarded-draft tokens). (Cost work has since moved to the cost_mode: budget profile - see Added.)
  • Actually claim the DeepSeek prompt cache. The new openrouter_providers setting pins the OpenRouter upstream order (ships DeepSeek) so the prompt cache hits - ~80% of the prefix at ~3.5× lower cost vs. default load-balancing; provider=deepseek direct guarantees it.
  • Diagrams: diagram_engine: auto now defaults to the built-in engine. It measures text and lays figures out compactly; D2 + ELK is explicit opt-in (diagram_engine: d2) since it tends to render wide. The zero-dependency built-in engine stays default and fallback.
  • use_researcher now defaults on - citations are unverifiable otherwise.
  • Critic routed to deepseek-v4-pro - insight scoring and thesis checks need the pro tier. Writer temperature set explicit (0.9); humanizer dropped to 0.3.
  • PDF page size A5 → A4 so code fits.
  • Hardened LLM calls: classified retry with backoff, fail-fast on 4xx, request timeout, and a structured-output repair retry.
  • pyproject.toml is now the canonical dependency source.
  • Structured-output truncation on the reasoning tier. A reasoning model (deepseek-v4-pro) can spend its whole budget thinking and return empty / cut off with finish_reason=length; the old retry re-sent the same too-small budget before degrading to the flash fallback. complete_structured now raises max_tokens and retries the same model, and models.yaml ships a max_tokens floor for the judgment nodes (critic/judge/verifier = 8000). Validated on the first real OpenRouter run (the DeepSeek prompt cache also confirmed engaging at 36% of prompt tokens).
  • autonomous: true was silently ignored by new (a store_true default shadowed it); --autonomous is now tri-state with a --no-autonomous override.
  • Post-completion revise now critiques with pipeline-parity context (watch-list, intake requirements, prior-unit context, length target).
  • CI install failure on Ubuntu (svglib 1.6 → pycairo) fixed by pinning svglib<1.6.
  • PDF exports with no images (default, no cairosvg) now render SVG diagrams as vector art via svglib; Wikimedia image search v1/v2 shape mismatch fixed; long chat replies no longer duplicate in scrollback; read --manuscript resolves article paths; the test suite no longer pollutes real telemetry.
  • The conversational assistant can no longer auto-execute delete / /user / /set; ids validated, deletes confined to the brain dir.
  • Exported HTML is sanitized (script/iframe/event handlers stripped).
  • Deep-research fetches are SSRF-guarded, robots.txt-respecting, and per-host rate-limited.
  • Context-compression “headroom” (use_headroom) removed entirely. It saved ~nothing on single-turn payloads and hurt the DeepSeek prompt-cache hit rate by perturbing the cacheable prefix. The setting, the [headroom] extra, and the llm.py code path are all gone; prompt-cache pinning (openrouter_providers) plus cost_mode: budget are the cost story now.
  • Dead vertical-slice prototype (run.py, src/writingagent/slice.py).
  • Initial book + article pipelines: plan → write → critique → revise → humanise → commit, with canon/consolidation, learned craft skills, and six export formats.