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.
Unreleased
Section titled “Unreleased”- Local web dashboard (
writing-agent web). A browser UI over the same engine the TUI drives - pure-stdlibThreadingHTTPServer+ Server-Sent Events + a single-page app insrc/writingagent/webui/.writing-agent web [--port N] [--no-browser](default port 8787, auto-opens the browser). Binds127.0.0.1only, 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 finishedwrite(gated byauto_promote); the target keyword is threaded into the writer up front (seo_keyword) and the H1 title is optimized after validation. Writesseo_report.md+keywords.json. It only edits the article and names keywords/hashtags - it never posts or schedules anything anywhere. Pin a keyword withwriting-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,linkedinselects formats; runs automatically after a finishedwritewhenauto_promoteis on. Generates LOCALpromo/*.mdartifacts only - nothing is posted or scheduled anywhere. - Cost - budget mode (
cost_mode).cost_mode: budget(the shipped default inconfig/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_tokensremains 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(needsFIRECRAWL_API_KEYin.env) as an alternative to the defaultduckduckgo; 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.mdv2). 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 (defaulteditorialskin - “ink & brass”: gold primary + brass + manuscript-red status). Named themes recolor, never restructure. Spec lives at the repo rootdesign.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 ⊃ skillsselects 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/agenticand/tracecommands surface the mode and the learned verdict; settingsagentic,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.reconcileprefers a smoothed, sample-gated duel win-rate over the first-pass fallback, and/skillsshows it. Addsskill_distill(deterministic, non-destructive retirement of near-duplicate skills; off) andwatch_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
highcontrasttheme (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 bypolish, regenerated on demand by the newevidencecommand (Project.evidence_report()). - Clean references, citations & figures (deterministic polish). A pure-Python pass (no LLM, ~0
tokens) builds one end
## Referenceslist 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 newpolishcommand 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_A11Yline-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. Newjudge/verifiermodel slugs andtournament_judge/verify_claims/table_read_revisesettings, 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. writingagentnpm launcher - a zero-dependency, globally installable Node CLI (npm install -g writingagent) that forwards to the Python engine. Awritingagent setupbootstrap 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 updatepulls the latest,writingagent doctorshows how the engine was resolved.- Shipped defaults flipped to match common usage - new projects default to
mode: article,autonomous: true(never pause for review), anduse_images: true. - Public Python API - a stable
Agent+Projectfacade (create, run, resume, revise, evaluate, export) plus a one-shotwrite(), 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 amin_insightgate plus clarity/structure/evidence scores and deterministic style metrics; a surgical humanizer that rewrites only flagged sentences. - Trust machinery - version snapshots (
<project>/versions/) withversionsandread --v K;revise --chapter Nto 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. writecommand - 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_COLORmode. - 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.
Changed
Section titled “Changed”- Internals reorganised into packages (behavior-preserving). The two largest modules were split
behind stable facades, so every
orchestrator.X/shell.Ximport is unchanged:orchestrator/(common · book · article · export · manage · review) andshell/(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 rawRuntimeError: …. /featureslists the new toggles (skill_duels,skill_distill,watch_blocking); live-run controls wording clarified - all interrupts are resumable (/deletediscards).- Diagrams: structured spec → deterministic renderer. The model no longer emits SVG (it can’t
measure text, so labels overflowed); it now returns a structured
DiagramSpecand 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-indivergent_skeletons(draft variants short, expand only the winner - ~60% fewer discarded-draft tokens). (Cost work has since moved to thecost_mode: budgetprofile - see Added.) - Actually claim the DeepSeek prompt cache. The new
openrouter_providerssetting pins the OpenRouter upstream order (shipsDeepSeek) so the prompt cache hits - ~80% of the prefix at ~3.5× lower cost vs. default load-balancing;provider=deepseekdirect guarantees it. - Diagrams:
diagram_engine: autonow 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_researchernow 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.tomlis 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 withfinish_reason=length; the old retry re-sent the same too-small budget before degrading to the flash fallback.complete_structurednow raisesmax_tokensand retries the same model, andmodels.yamlships amax_tokensfloor 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: truewas silently ignored bynew(astore_truedefault shadowed it);--autonomousis now tri-state with a--no-autonomousoverride.- Post-completion
revisenow 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 --manuscriptresolves article paths; the test suite no longer pollutes real telemetry.
Security
Section titled “Security”- 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.
Removed
Section titled “Removed”- 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 thellm.pycode path are all gone; prompt-cache pinning (openrouter_providers) pluscost_mode: budgetare 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.