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.
0.3.0 - 2026-07-18
Section titled “0.3.0 - 2026-07-18”Changed
Section titled “Changed”- Repository restructure: clean root, one home per thing. Spec docs moved under
docs/(plan.md,design.md,prd.md,learning.md,roadmap.md,proposals/); the Gradio Hugging Face Space demo renamedweb/→demo/(no longer confusable with the package’swebui/);SampleRun/moved toexamples/sample-run/(the bulkymanuscript.pdfdropped - the markdown output shows the same run); shipped data single-sourced undersrc/writingagent/resources/(gold/,personas/,seeds/,models.yaml). The repo root now holds only standard community files +pyproject.toml. - BREAKING - runtime state moved out of the repo to the agent home.
brain/,.index/,config/models.yaml+config/settings.yaml, and the/setkey.envnow live under the agent home:$WRITINGAGENT_HOMEif set, else the OS user-data dir (%LOCALAPPDATA%\writingagenton Windows,~/Library/Application Support/writingagenton macOS,$XDG_DATA_HOME/writingagenton Linux). Running the tool from any directory no longer scatters state there, and pip installs stop writing config next to site-packages. To migrate an old checkout, move those files/folders from the repo checkout root into the agent home. - BREAKING - the repo-root launcher
writingagent.pywas removed. Use the installedwriting-agentcommand orpython -m writingagent.
- Web dashboard accessibility + polish pass. Keyboard access for the primary surfaces
(sidebar nav is real buttons; project rows and approach cards are focusable and
Enter/Space-activatable; settings inputs gained programmatic labels; toasts announce via live
regions; the run log is a
role="log"). Hash routing (#projects,#project/<id>): refresh and back/forward keep your place, and the tab title follows the view (per-view titles). The primary button’s label color is a themed token with a contrast guard inapplyWebTheme(), so a named palette can no longer produce an unreadable button. A finished run now shows an “Open the piece” completion banner instead of only a log line. Also: the Projects poll repaints only when data changes (no more 6s flicker), the banner side-stripe was dropped (tint + icon carry the state), and the project header rows align to the centered content column.
0.2.1 - 2026-07-17
Section titled “0.2.1 - 2026-07-17”- Shipped defaults bundled in the wheel. Pip installs used to get no
config/models.yaml(so no per-node routing and no fallback model) and no seed skills (seed-skillsinstalled 0). The package now bundles both:models.yamlis copied out on first run into a real, editableconfig/models.yaml, and the 13 built-in craft skills seed from the bundled copies. Sync tests pin the bundled files byte-identical to the repo originals.
Changed
Section titled “Changed”- Model-agnostic messaging + docs de-slop. The README and human-authored docs now lead with “any OpenAI-compatible host, no blessed default” rather than an OpenRouter/DeepSeek default, and the project’s own anti-slop rule (no em-dashes) was applied to the prose and the banner/architecture SVGs.
- Refactor + CI maintenance. The near-identical book/article run-op bodies were de-duplicated
(behavior-preserving); GitHub Actions bumped to current majors;
ruff check .is green repo-wide.
- Repo-wide docs consistency sweep, audited against the code. Command snippets, the
budget-mode default, the OpenRouter-only wording, the trace-policy data floor (≥10 per arm),
the diagram node’s 4k spec cap, and
SECURITY.md’s/set/outbound claims now all match the implementation.
Removed
Section titled “Removed”- The npm launcher. Removed entirely - it was never published or maintained. The Python install
(
pip install writing-agent,pipx, or source) is the only supported path.
0.2.0 - 2026-07-16
Section titled “0.2.0 - 2026-07-16”- 23 model hosts, no blessed default. First-party Anthropic, Perplexity, Cerebras, and SambaNova,
plus AWS Bedrock and Azure OpenAI via an OpenAI-compatible gateway - 23 hosts total, one wire
format (
providers.py). There is no default host: a first-run wizard detects a key already in the environment and offers it, or lets you choose a provider and paste its key; switch anytime with/provider. New/setkey [<key>]saves the active provider’s key to.envand applies it live. - PyPI release + open-source hardening. Release automation (Trusted-Publishing/OIDC on a
v*tag), CI coverage (Codecov) and a gitleaks secret-scan, Dependabot, README PyPI / Docs / Python / Platforms / License badges,CODEOWNERS,CITATION.cff,FUNDING.yml, andROADMAP.md. - Zero-install Gradio web demo (
web/app.py,pip install -e ".[web]"). A browser front-end over the publicAgent/Projectfacade: a free preview runs the whole flow offline (fake mode, $0, no key), or paste your own provider key for a real piece. Ships a Hugging Face Space config; the package imports gradio lazily. (Distinct from the localwriting-agent webdashboard.) - 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). The recommended opt-incost_mode: budget(the shipped default isstandard; enable with/set cost_mode budget) applies a per-unit token budget (budget_tokens_per_unit, default 20000) plus a fixed overhead so a full piece finishes instead of pausing, and routes the judgment nodes (critic / judge / verifier / consolidation / diagram) to the cheaper flash tier. An explicitmax_run_tokens(> 0) is 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 ≥10 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. - 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”- Natural language can run any slash command, including
/set. The chat assistant’s system prompt documents the full slash surface with NL triggers, and/setwas removed from the chat denylist so config requests (“turn on researcher”, “use the poe-gothic persona”) execute from plain English. Config is reversible and each executed line is echoed; only/useranddeletestay manual. - 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 (default unpinned/""; set it to e.g.DeepSeekto opt in) 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.
- Review-driven fix sweep (2026-07-16). A full-codebase review (redundancy · mismatches ·
optimization) drove one batch of confirmed-defect fixes across every subsystem - escalation score-array
desync, N-style citation preservation, register-aware surgical guards,
max_context_chars: 0honored, chapter-writer length-truncation recovery, learned-policy corpus no longer skewed byrevise, the agentic critique panel wired for books, SQLite connection leak on a corrupt db, web-dashboard raster images, whole-word emotion/SEO matching, plus dead-code removal. Suite green (523 passed / 1 skipped), ruff clean. - 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 auto-runs commands - including
/set, so plain-English config changes take effect (reversible, each executed line echoed back). Onlydelete(irreversible) and/user(switches identity) stay fenced - the human must type them. Ids are validated and deletes are 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.