What's new

Every release, in plain words.

Latest release 0.9.0, 8 Oct 2026. Each entry says what changed and why it mattered, written the day it shipped. Follow it in a feed reader, or watch the repository on GitHub.

Atom feed GitHub releases feed Releases on GitHub CHANGELOG.md
0.9.00.8.60.8.50.8.40.8.30.8.20.8.10.8.00.7.00.6.10.6.00.5.20.5.10.5.00.4.10.4.00.3.20.3.10.3.00.2.00.1.60.1.50.1.40.1.30.1.20.1.10.1.0
0.9.0release notes

Windows, Linux without systemd, a public site, and the batch brain on a monthly API credit.

  • The batch brain on an API credit. Claude Max and Team plans carry a monthly Claude Platform credit that pays for raw API calls, not for the interactive CLI. The anthropic provider of tools/llm.py now has what it takes to run the night on it: a cost ledger (.agents/state/llm_costs.jsonl, every call with its tokens and list price), a budget (api_budget_usd in brainless.toml; past the warn ratio the health check and the watchdog say so, past the stop ratio every API lane runs on its fallback until the reset day), a billing error class that hands an exhausted balance to the fallback, and python3 tools/llm.py --usage for the spend per lane. A key linked to an account rather than a workspace sends anthropic_workspace_id. Guide: docs/shed.md.
  • A system prompt for the raw API. The API carries none of the context the Claude Code CLI loads from the vault, so anthropic calls send _Agent-Context/LLM-SYSTEM.md (house rules, a few lines) plus the owner's language from the profile. Measured the same day: one empty claude -p call carries about 18k tokens of CLI context, which is why the credit goes through the API and not the CLI.
  • The CLI's own receipt. claude-cli calls run with --output-format json, so the ledger also records what the subscription absorbed, at the price the CLI reports, and the answer is read past any notice a tool manager prints first.
  • Shared git sync for the workers. git_sync.sh replaces the inline pull in the wrappers: it aborts a conflicting rebase, restores the autostash, alerts once per incident and exits non-zero while the tree is unsafe, so a job skips its run instead of committing conflict markers. A timer that fires in the seconds after wake waits for the network, and the worker pushes every hour.
  • One installer owns every unit. install.sh substitutes the vault path into each unit, sets BRAINLESS_VAULT, skips the owner-only units unless asked (owner-only.txt), and seven timers gained Persistent=true. A test parses every unit and fails on drift.
  • Engine hygiene. fsutil.atomic_write behind every live wiki page, one timestamped log line (logline.py), one frontmatter parser, one JSON reader, utf-8 named on every file open for the Windows port, ruff in CI, a generated script index in docs/scripts.md with a test that fails when it goes stale. Six bugs from a code scan fixed (a Buzz reply with media, the lite path importing fcntl, a crashed job's lease, the compile digest depending on the machine, Telegram redirects re-checked on every hop). Dead code and a June-old archive removed; the entity phase reads the corpus once per run.
  • The AI shed. The always-on worker as a home for standing agent panes: brainless-herdr.service runs a headless herdr server, attach from a laptop with herdr --remote or a saved machine, from a phone with any SSH client. docs/shed.md covers the billing split (what runs on the credit, what stays on the subscription), the three layers that keep the credit the only money spent, and the first week's checks.
  • A public site. docs/ now serves as a GitHub Pages site: a front page on the kilim brand with the loop, the six voices, the Medium and X writing and the install lines, and a what's new page with every release in plain words. tools/build_site.py writes docs/whats-new.html and the Atom feed docs/feed.xml from this changelog; run it after editing this file (--check fails CI when they are stale).
  • Night dialectic experiment closed. brainless-dialectic-night.timer is removed: after 11 nights the local 4B model never produced a usable night (22 of 132 persona replies passed, best night 6/12 against a bar of 8/12). The personas collapse into one voice. --run night and its service remain for manual runs. Result in docs/local-inference.md.
  • Windows install. irm .../install.ps1 | iex in PowerShell does what install.sh does: checks git and Python (it skips the Microsoft Store stub), clones, makes the venv, puts brainless on your PATH and runs brainless init. It works in Windows PowerShell 5.1, the one that ships with Windows.
  • One command line for every platform. The commands moved from the bash script into tools/cli.py. bin/brainless and the new bin/brainless.cmd only find the vault and its Python.
  • Keys in Credential Manager on Windows, through the system API, never on a command line.
  • A Task Scheduler task for the background runs: at logon and every 15 minutes, with catch-up after sleep, on battery too, and with no console window popping up.
  • Linux without systemd (WSL, containers, some distros) gets one crontab line. brainless doctor crashed there before.
  • No more fcntl on the lite path. The compile reached it through the concept review, so Windows failed at import. A small portable lock replaces it.
  • Outside folders link as a directory junction on Windows when a symlink needs admin rights. Ollama installs through winget when you say yes.
  • CI installs on Windows as well, and runs the scheduled task once.
0.8.6release notes

Runs that did nothing now say so.

  • A failed model call fails the run. weekly_reconcile.py and evening_closeout.py returned 0 when the model gave nothing back, so the run log read "ok" over a drift report that was two weeks old. Both now exit 1, name the cause (timeout, auth, error) and report report=0 or closeout=0. An idle day is still a clean exit.
  • Scans are parked, not retried for good. A document with no text in it, or in a format the converter does not know, failed the same way every week and raised the same notification. smart_processor.py now parks it: one notice, then nothing until the file itself changes. The empty _raw.md a failed conversion left behind is removed, and records of files that are gone are pruned.
  • The briefing is pushed within the hour. With two machines, the laptop held the morning briefing until the evening backup while the worker's 21:00 close-out created the same file. Two new files on one path, and every pull failed. vault_backup.sh --only <path> commits and pushes named paths alone, with the same remote guard and retry, and cron_wrapper.sh uses it for today's briefing.
  • Scores no longer add up. The run log summed every number in a day, so two compiles at hit@5 88 read 176 and the next single run looked like a fall. Scores and levels keep the day's last value; tallies still add up.
  • An idea is written once. The compiler took whatever slug the model chose, night after night, and one idea ended up on four pages. The prompt now names the ideas already held, and a proposal is skipped when a page has its slug, answers to its title or alias, or was archived by a merge. Existing idea pages are no longer rewritten.
  • lint --fix-links follows titles and aliases. A link that names a page by its title, an alias or another spelling is repointed when one page alone answers to that name, and keeps its wording as the display text.
  • Fenced drafts survive the dialectic. The splitter read a heading inside a fenced proposal as a section break, cut the draft and lost its closing fence, so the rest of the filed page rendered as code.
  • The pile count skips machine ledgers. Inbox/CRM, the per-organisation log that crm_capture.py keeps, no longer counts as Inbox files waiting on anyone.
0.8.5release notes

Docs, and one line of tidying.

  • Where the logo is set, written down. Design now says how the icon reaches the three places outside the repo: the GitHub social preview (by hand, no API), the Telegram bot (setMyProfilePhoto) and the Buzz agents (the installers). A manual Buzz profile write must repeat the name and about text, or they are wiped. The reference deployment points to it from the persona install step.
  • One deny rule less. The persona settings no longer deny MultiEdit, a tool Claude Code no longer has. Nothing else in the sandbox changes.
0.8.4release notes

Two fixes to the new brand.

  • The social card keeps the whole name. The 0.8.3 card was cropped to 2:1 and lost the b of brainless. It is now the full banner, padded top and bottom with its own night-blue ground.
  • Buzz agents keep their avatar. The installers set the kilim icon together with the display name. A Buzz profile update replaces the whole profile, so a reinstall that set only the name would have wiped the picture. BUZZ_AVATAR_URL points it elsewhere.
0.8.3release notes

A face. No behaviour changes.

  • A new mark: the kilim brain. Anatolian weaving motifs on the nodes of a network, notes as motifs, links as threads. The README and the wiki open with the banner.
  • One palette, written down. docs/design.md holds the design point of view: eleven colours with their roles, logo rules, terminal colour rules, and CSS variables for an interface that does not exist yet. tools/brand.py is the code copy.
  • Logo files for every place a logo goes: SVG and PNG mark in four sizes, WebP, a square icon on night blue for avatars, a favicon, the banner, and a 1280x640 social preview, all in docs/assets/.
  • The installer speaks in the same colours, and only on a real terminal. NO_COLOR, pipes and TERM=dumb get plain text as before.
0.8.2release notes

Security. A full audit after the repo went out more widely; if you run an earlier version,

update.

  • A file name can no longer run commands on a Mac. Notifications put the name of a document that failed to convert straight into AppleScript, so a file named with a quote and do shell script ran a shell command on the next hourly job. The text now reaches osascript as an argument, never as script. Same fix in health check, worker reach and the compiler.
  • Automated Claude calls get exactly the tools they are granted. A deny list left Read, Glob and Grep open, so the research lane could read the vault and hand it to WebFetch if a page it read told it to. Every call now passes --tools with its grant (none by default, WebSearch and WebFetch for research, Read for photos) and --strict-mcp-config, so the MCP servers you use interactively (mail, chat, drives) never load in a timer.
  • The personas and writing agents read only your vault. Their settings named the author's home directory, so on anyone else's machine nothing was denied, and the persona installer did not template them. Read is now scoped to the vault, every dotfile in home is denied (keys, tokens, shell history), and the unit, env file and team rules are filled in for the installing user. The writing agents lost WebFetch: an injected page could otherwise carry vault text out in a URL. WebSearch stays.
  • The backup never pushes a vault to a public repo. It commits everything, and a clone of the engine (or a public fork) has the public repo as origin. It now pushes only when GitHub answers 404 to an anonymous request for the remote, and keeps the commit local otherwise.
  • The leak scan passes when clean. It no longer reads __pycache__, and its private word lists live next to the registry instead of in the shipped script.
  • Examples and fixtures use invented names and topics.
0.8.1release notes

What the first full lite install found, from the published one-liner to an uninstall.

  • A second digest on the same day no longer replaces the first. A laptop can digest twice in a day; the later one is now appended to the day's file. Before, the earlier digest was lost while its notes were already archived.
  • Chatter in front of a digest is cut, and caught. A digest that opened with the model explaining it could not search the wiki was saved as it came. The page is now kept and the preamble dropped, and the guard knows the new phrasing. The stricter check also finds older damaged pages, which lint now lists.
  • Search returns your notes before the command prompts. "What did I decide about price" used to rank the /decide instructions first. .wiki/_commands/ is out of the search.
  • The nightly change brief works on a lite clone. It listed nothing, because it skipped every page git ignores. It now keeps them when the brief itself is untracked.
  • Generated wiki files stay out of git. The index pages, which name your documents, and the lint report were visible to git add . in a public clone.
  • Aliases in your language. An English vault got Turkish aliases; they now follow the vault's language, with English added for other languages.
  • init says which vault the command opens. When another vault is already the default, the closing screen says how to use the new one. The Lite guide says where the day's notes go after the digest.
0.8.0release notes

The lite profile: brainless on one computer, with a folder of notes and any model. It is

now what the installer sets up. The multi-machine setup is install.sh --profile full.

Guide: docs/lite.md.

  • Concepts decided in the terminal. brainless concepts review shows each waiting proposal and takes yes, no or skip (evet, hayır, atla); brainless concepts decide does one from a script. The same effect as a Buzz reply.
  • A lite command set. The lite profile hides the commands that assume the full setup; brainless profile lists them and brainless profile enable <command> turns one on.
  • An install test on every push. A second workflow installs on macOS and Ubuntu against a stub model and runs doctor --probe, add and tick.
  • Docs. docs/lite.md; the README install section now describes brainless init.
  • brainless.toml. One settings file at the vault root (tools/config.py). It fills the existing BRAINLESS_* variables as defaults, so the environment still wins and a vault without the file behaves as before. brainless config show prints what it sets. Template: brainless.toml.example.
  • Keys in the OS keychain. brainless config secret set llm_api_key stores a key in the macOS Keychain, libsecret on Linux, or a mode 600 file elsewhere. A key written into brainless.toml is ignored with a warning.
  • Folders by role, not by name. tools/paths.py resolves inbox, library, thinking, daily, beliefs, decisions and ideas from [folders], so a vault can call its notes Notlar. Compile, digest, dialectic, calibrate, concepts and media import read the roles. Localised names for new vaults live in tools/locale/<lang>/folders.json.
  • Any model. New providers in tools/llm.py: anthropic (API key), ollama, openai, grok, gemini, openrouter (each with its own key, never another's), gemini-cli and codex-cli (your own sign-in, run from an empty folder, Codex in its read-only sandbox). One model setting, BRAINLESS_LLM_MODEL, per lane if needed. llm.py --models <provider> lists models. Ollama and loopback endpoints count as local, so the no-cloud-fallback rule covers them.
  • brainless init. The installer now asks instead of assuming. install.sh sets up Python and the command, then hands over to brainless init: your name and language, folders named in that language, other notes folders to read (linked, never changed), the model (Ollama, which it can install; an API key, kept in the OS keychain; or a CLI you are signed in to), one test call, optional semantic search, the background entry and the permissions it needs, and a first run if you want one. --yes with flags runs it unattended. Your folders go into .git/info/exclude, so notes never reach git. Python 3.11 is now the minimum. install.sh --profile full keeps the old setup.
  • brainless doctor. One line per check: folders, model, key, Ollama, scheduler, failed jobs, last compile. --probe sends one tiny prompt.
  • One machine, one queue. brainless tick runs what is due from a SQLite queue: convert hourly, digest and compile each evening, lint and prune weekly. A night the laptop slept through runs at the next wake; jobs that need the network or the model wait for them; failures back off and then show in brainless queue. brainless schedule install adds the single launchd, systemd or Task Scheduler entry that calls it, on the lite profile only. brainless add captures a note or a file. On the lite profile a document dropped in the inbox is converted there.
  • Concept proposals wait without Buzz. On a machine with no Buzz identity they stay pending instead of going to an outbox nobody delivers.
  • Summaries without write access. smart_processor.py no longer lets the model write files: the model returns text and the script writes it. Book summaries run on any provider; photo OCR still needs the claude CLI and leaves photos in the inbox otherwise.
  • The research lane says when it cannot search. Without the claude CLI a web lane is skipped with a note instead of failing.
  • The vault is where the engine is. With BRAINLESS_VAULT unset, tools use the checkout they run from instead of a fixed home path.
0.7.0release notes

Take a bad source back out, one document per source, concepts decided in Buzz, and

frontmatter that Obsidian can read.

  • Retract a source. tools/retract_source.py traces a source that turned out wrong: its summaries (raw twin and book folder included), the concept pages that cite it, the pages that link to it. With --apply --reason "..." it goes into _Agent-Context/retracted.md and the compiler never compiles it again. A concept claim that stood on it alone is struck and kept in Superseded, marked retracted with the reason; a claim with other sources keeps them. The validator lets only the retracted links go, and a page it refuses stays as it was and is named for a hand edit.
  • One document, one source. A raw conversion beside its cleaned file, and a reading sheet beside a book summary, stop compiling as separate sources. Their old summaries go to .wiki/_archive/summaries/ with the reason in LOG.md, and links move to the copy that stays. Concepts read one summary per document.
  • Concepts are decided in Buzz. tools/concept_review.py posts each concept proposal to #dreaming; reply evet, hayır or atla. The Sunday proposal pass reads the whole catalogue and stops while five proposals wait.
  • Contradiction check. tools/contradiction_check.py reads each night's new summaries against their nearest pages and writes real conflicts to _Agent-Context/CONTRADICTIONS.md and the change brief. No page is edited.
  • Run log fix. The run log dropped any line with a digit in a key, so a night of failed calls could read as ok. Fixed, and a partial run now warns in health.
  • Quoted frontmatter. A colon followed by a space in a title or summary made the whole frontmatter block invalid YAML, and Obsidian dropped the page's properties. The tools never noticed because they read values by regex. tools/file_query.py and the idea writer now quote a value when it needs quotes (concepts.yaml_scalar), and every page the model writes passes through concepts.quote_frontmatter before it lands: summaries, projects, digests and concept pages. Lists, block scalars and quoted values stay as they are, and a second pass changes nothing. A # inside a summary, which YAML reads as a comment and cuts off, is quoted too.
  • Readers strip the quotes. concepts.fm_value and lint_wiki.parse_fm return the value without its quotes, so search, the index and the graph export see the same text as before.
  • Repair existing pages once, from the vault root: python3 -c "import sys;sys.path.insert(0,'tools');import concepts as C;from pathlib import Path;[p.write_text(n) for p in Path('.wiki').rglob('*.md') if (n:=C.quote_frontmatter(t:=p.read_text()))!=t]"
  • Test: tools/tests/test_file_query.py.
0.6.1release notes

Health sees the worker's LLM login.

  • Worker LLM row. The LLM breadcrumb (.agents/state/llm_status) is per machine, and the worker does the heavy LLM work. tools/worker_reach.py now copies the worker's breadcrumb over ssh each hour, with the worker's own file time, into .agents/state/worker_llm_status.json. health_check shows it as a second LLM row, red on an authentication error, so an expired worker login no longer hides behind the main machine's green row. BRAINLESS_WORKER_VAULT sets the worker's vault path, relative to its home (default projects/brainless).
0.6.0release notes

The nightly compile says what it did.

  • Change brief. tools/wiki_changes.py snapshots .wiki/ before the 23:20 compile and diffs it after: pages added, updated and removed per kind, links gained, and new Contested or Superseded bullets on concept pages. No model. It writes _Agent-Context/WIKI-CHANGES.md, the morning briefing reads it, and a new contradiction becomes a quick action for the owner. --since <git ref> shows the change since any commit.
  • A stuck compiler shows in health. The run log gets wiki_changed; seven nights in a row with no page changed turn HEALTH.md yellow. The dry-run check now counts only runs that report the field, so a field added later does not read older runs as zeros.
  • Dialectic backstage. The persona rounds move to #dialectic-lab; the owner's #dialectic gets one short card per topic. The long context goes to a brief file the personas read, so moderator messages stay a few lines.
  • Telegram receipt. Capture sends one "saved" line back to the whitelisted chat; still no commands or buttons. Buzz deliveries drop the hidden marker, which Buzz rendered verbatim.
  • Leak scan. Common words that are also entity aliases no longer flag every file.
0.5.2release notes

The README says what it runs on, and the root shows the engine.

  • What it runs on. A new README section names the three devices behind the reference setup: an iPhone for Telegram capture and Buzz replies, a Mac for writing and commands, and an always-on Arch Linux laptop behind Tailscale that runs the timers, the capture, the Buzz relay and the persona agents. One laptop is still enough to start.
  • Reference deployment is its own doc. The real setup (devices, diagram, schedule, units, persona sandbox, placeholder secrets) moved out of docs/how-it-works.md into docs/reference-deployment.md, and the wiki page is now generated from it instead of being written by hand. How-it-works keeps a one-paragraph pointer.
  • The root shows the engine. The eleven empty content folders (Work/, Inbox/, Thinking/Daily/ and the rest) no longer ship with a .gitkeep each; install.sh makes them on the first run. The repo root goes from thirty entries to twenty one.
0.5.1release notes

Shipped files name nobody real.

  • Invented names in examples. Test fixtures, locale self-tests, comments and one prompt example named real people and companies. They use invented names now; the Turkish suffix cases still test the same thing.
  • MCP deny list from the profile. tools/mcp_server.py builds its second deny list from fixed parts plus private_segments in PROFILE.md, folding case, accents and separators. Put a family member's folder name there and the server will not serve it.
  • Leak scan checks tracked names. tools/export_public.py reads the entity registry, the entity candidates and the Work/*/About People/ file names at scan time and flags any of those names in a shipped file. The names never enter the code; in a checkout without those files the check has nothing to look for.
0.5.0release notes

Search by meaning, and a wiki that proposes its own links.

  • Hybrid search. wiki_search.py fuses BM25 with local embeddings (tools/semantic_index.py, multilingual-e5-large through fastembed, on the CPU; no vault text reaches an API). On the 34 golden questions MRR rose from 74 to 87 and hit@1 from 62% to 79%, better on every kind of question. An optional addon: pip install -r requirements-search.txt, then python3 tools/semantic_index.py build. Without it, search is BM25 as before. A smaller model and a reranker were measured and rejected.
  • Passage-level sources. Every result carries the passage that matched, the line of the match and the last [mm:ss] marker before it, so an answer cites path:line. The snippet now folds Turkish letters on both sides; a Turkish query no longer shows the frontmatter.
  • Retrieval eval by kind. The golden set grew from 22 to 34 questions, each tagged name, paraphrase, crosslang or claim, and the eval scores each kind apart and records the mode.
  • Read-only MCP server. tools/mcp_server.py gives any MCP client search, read_page and index. Standard library only. Over stdio, or over HTTP bound to one address with a bearer token (brainless-mcp.service for the worker). It never serves a page git ignores, and a hard-coded deny list backs that up. It cannot write.
  • Link suggestions. tools/link_suggest.py proposes links between pages that mean the same thing but do not link: mutual nearest neighbours in the top 1% of pairs. Homes for orphan pages first, then new links, then near-identical pages for the dedupe procedure. Report only, gitignored and out of the graph.
  • Dreaming. tools/dreaming.py runs at 04:00 on the worker. A model judges each suggested pair (link, duplicate or none, with one sentence why) and posts up to five a night to Buzz #dreaming. The owner answers yes, no or skip. Approved links live in _Agent-Context/links.md, and the compiler replays them on every compile, so a rewritten summary keeps them. Duplicates become merge rows; nothing is deleted. A kill rule stops it when under 30% of proposals are approved. Setup: .agents/buzz/install_dreaming_channel.sh.
  • Documents to the inbox. A PDF or office file posted to Buzz #inbox is converted with markitdown on the worker into Inbox/Documents; the original never reaches git.
  • Worker reach. tools/worker_reach.py tells the Mac when it loses the worker (Tailscale down, ssh failing, or no worker commit for hours), with a local notification and a HEALTH.md row. The worker commits its run log twice an hour, not every run.
  • The wiki compiles every night. The compile, lint report, search index, link suggestions and retrieval check moved out of the nightly digest into their own job (tools/nightly_compile.py, brainless-compile.timer at 23:20). They used to run only on nights with captures, so a quiet day left edits uncompiled and the index stale.
  • The installer steps past systemd units already linked in place. The Mac's nightly sync refreshes the search index and the link suggestions.
0.4.1release notes

Version 0.4.1

  • _Agent-Context/LEARNINGS.md: the owner's standing preferences and the lessons from correcting an agent, each with the rule, why, and how to apply it. They live in the vault, not in one tool's memory. The SessionStart hook loads the file, and the writing and narratives agents and the Buzz reply worker read it. AGENT-RULES.md Rule 1 names it. The public export ships a stub with one example entry.
0.4.0release notes

Version 0.4.0

  • Concept pages. A summary says what one source said; a concept page says what the vault knows about one idea, and every new source updates it in place. - A disagreement keeps both positions with sources and dates. - An outdated claim is struck through, never deleted. - Every claim carries a confidence label. - tools/concepts.py validates each rewrite. - New concepts are proposed into _Agent-Context/concepts.md and wait for a yes. - The old articles phase is retired, and --only concept-migrate moves its pages over.
  • A connected graph. - A deterministic linker ties summaries to the people, companies and concepts they mention. It holds back when a first name belongs to someone else. - INDEX.md gives one line per page, with topic indexes in .wiki/_index/. - Aliases in _Agent-Context/aliases.md catch the words people actually use. - Search folds Turkish letters. - A project mirror that shares a name with an entity becomes "(project)".
  • Measured. - tools/wiki_metrics.py tracks the orphan rate, links per page, the main component and the bridge pages, week on week, in the scorecard and in HEALTH.md. - tools/retrieval_eval.py scores golden questions every night. - tools/wiki_dedupe.py proposes merges and never merges. - brainless graph writes a Gephi file coloured by page type.
  • Checks, not requests. - Claude Code hooks (tools/hooks/claude_guard.py) ask before human-area writes and deletions, and refuse secrets and misplaced briefings. - tools/output_guard.py refuses model chatter and nested pages before they are written. - tools/run_log.py records every scheduled run with its counts, so a job that ran and did nothing shows up. - The nightly job no longer swallows a failed digest or the compiler's exit code.
  • New doors. - brainless media turns YouTube and Apple Podcasts episode links into timestamped transcripts, queued and transcribed locally. - brainless chats brings Claude or ChatGPT history in, triaged and filtered first.
  • Backups. brainless backup seals the vault and its history with age once a month, into a folder sync does not reach. verify is the restore test.
  • Answer rule. Answers from the vault cite a page for every claim, keep outside knowledge under its own heading, and end with what was read and what is not covered.
0.3.2release notes

Version 0.3.2

  • The docs follow the README's four lines. Each page opens with the line it serves (gather, think, decide, get shit done) or says it is setup. docs/how-it-works.md is regrouped under the four lines with a new get-shit-done section, and docs/scripts.md names its sections the same way.
  • "Every command proposes and you paste" becomes "the machine proposes and you apply", and "the same five steps" becomes "the same loop", wherever they appeared.
  • Link labels in docs/commands.md, docs/thinking-clearer.md and docs/scripts.md read as page names, and docs/syncing.md points at the reference setup in docs/how-it-works.md instead of a README section that no longer exists.
0.3.1release notes

Version 0.3.1

  • The README is rewritten for people, not for the code: the problem, the solution and four lines (gather, think, decide, get shit done), then the install. The long technical half moves to docs/how-it-works.md and the wiki, where the README links.
  • docs/references.md: the books, papers and tools the method, the voices and the engine are built on, each with what was taken and where it shows up. Also a wiki page.
  • setup.sh is gone; install.sh has done its job since 0.1. Unused weekly-question labels are dropped from tools/locale/*/thinking_loop.json.
0.3.0release notes

Version 0.3.0

  • Writing and narrative agents: .agents/buzz/install_agent_channel.sh <slug> installs a conversational Buzz agent with its own channel from a card in .agents/buzz/agents/, pinned to a model and effort level (BUZZ_AGENT_MODEL, default claude-fable-5-1). Two cards ship: Writer (#writing, long-form pieces) and Narrator (#narratives, children's stories). Each writes only inside its drafts folder, loads the editing files first and runs the editor lint before every draft.
  • tools/writing_index.py compiles _Agent-Context/WRITING.md, the map of every long-form asset; tools/writing_ideas.py posts a pitch round to #writing every second month. New PROFILE.md fields: editor_dir, writings_dir, drafts_dir, narratives_dir, longform_dirs, corpus_dirs.
  • The polling reply worker leaves harness channels alone (HARNESS_CHANNELS), so an owner message in #writing gets exactly one reply.
0.2.0release notes

Version 0.2.0

  • Telegram is capture-only; receipts and errors arrive in Buzz #inbox. Nothing is sent back over Telegram any more: no replies, no buttons, no /today. Old Telegram message ids stop authorising writes; pending previews are re-posted to Buzz and must be approved there. This is the reason for the minor version bump.
  • Today and weekly thinking use owner-only Buzz threads with revision-bound approval, pending-preview migration and recoverable writes.
  • Notifications use a durable Buzz outbox with relay acknowledgement and lost-ack reconciliation. The interaction timer retries failed delivery and saved replies.
  • Added delivery, workflow and capture-only regression coverage. See Buzz interactions for cutover and recovery.
  • Give the nightly compile a wall-clock budget so a backlog at the front cannot eat the whole window. Summaries, projects and entities check the clock before each file; articles and ideas reserve the seconds their one call needs. The index always runs. The nightly passes most of its timeout to the child as --budget-seconds and runs it unbuffered, so a killed run still leaves a journal of where it stopped.
  • health_check.py notifies with osascript on macOS and notify-send on Linux, and skips the call when neither is present, so a RED report on the worker no longer crashes the run after writing the file.
  • build_dashboard.py reads last-log dates from the Log section only, and counts both ### YYYY-MM-DD headings and - [YYYY-MM-DD] list lines. A project that dates its log as a list no longer looks months stale.
  • The method page now lists the seven Quivy steps with the file each one maps to, and adds the seventh (the pass ends in a seed and, when it settles one, a decision note), which the research command already enforced but the page never said. The dated audit it used to carry moves here: on 2026-09-19 all ten seeds in Thinking/Ideas/ lacked a zk: address while all six compiled ideas had one, the wrong way round; tools/zk_id.py --apply is the fix. The page no longer cites a tool that does not ship (editor_lint.py), names a reading route for Zettelkasten, and the README's Install section gains an "Optional inputs" note for voice (ffmpeg + whisper.cpp) and photographs (a backend with vision).
  • Count the pile, then drain it. tools/wiki_prune.py --count writes _Agent-Context/PILE-SCORECARD.md every Sunday with no model: captures per graded decision, filed analyses per decision, Inbox files past fourteen days (the capture belief's own falsification line), orphan wiki pages, concept articles that bridge two homes, decisions and challenged beliefs in the window. --archive applies mechanical rules with a stated reason, dry run by default: an unlinked analysis after 30 days, a summary whose source left the compiled roots after 60, any orphan after 60, and a meeting report that has been summarised and mined after 14 days in Inbox. Nothing is deleted; every move is logged to .wiki/_archive/LOG.md with its rule and reason, and the archive is out of search, index and lint. health_check.py carries the Inbox count into the briefing, red above ten. lint_wiki.link_graph() is now the one definition of "linked" for lint and prune alike.
  • The nightly digest reads the epic/story/task label before it reads the note: a task contributes its action items and no narrative, a story three lines, an epic is read in full. The digest runs the classifier first so tonight's captures are labelled tonight, caps the raw text it reads, and the task ledger applies the same near-duplicate guard the meeting extractor had (tools/task_dedup.py), so a promise rephrased by the model does not become a second row.
  • A sixth persona, Methodologist, built on Quivy and Van Campenhoudt's research method: it rewrites the thesis as a research question, names the hidden angle, builds concept, dimension and indicator, and writes the falsifiable hypothesis and the cheapest observation plan. Every persona now receives what the moderator received (the topic's wiki hits, the owner's core beliefs, the decision calendar) and round two must cite a vault file. Six sessions at once strain a single subscription, so personas are mentioned one at a time by default; --parallel restores the old behaviour.
  • Each dialectic topic is filed as two pages and a folded transcript. Page one is what a decision needs: a verdict computed from the final votes (Go, Stop or Test first, with the median number), the moderator's conclusion and fields, a compact vote table and the proposal. Page two is the method trace: the research question, the hypotheses and tests, one row per persona with its finding and objection. The raw rounds sit under a collapsed callout in the same note. The unanimity warning is a callout, not an HTML span. /dialectic produces the same layout.
  • --run night and brainless-dialectic-night.timer: a 02:00 experiment that runs the persona lane on the worker's local model while the moderator and a new dialectic-judge lane stay on the cloud and grade each local reply. Replays the day's first topic for a like-for-like comparison, scores nothing, and keeps its own section in the status file with a five-night kill rule.
  • Add epic-triggered weekly research. tools/note_classify.py tags every capture in the daily and meeting folders as epic, story or task in the Atlassian sense, and tools/weekly_research.py runs a research cycle only when the week produced an epic. A week of stories and tasks costs nothing: the job exits in seconds without calling a model. The epic gate is deliberately conservative (a score, several independent signals, and either recurrence or breadth), targeting zero or one epic per week, and an epic already researched within eight weeks is not researched again: the recurrence is reported instead, because a topic that keeps returning without closing is waiting for a decision, not for more evidence.
  • Research follows the vault's Quivy method: one opening question, the lens named in one sentence before any hypothesis is written, falsifiable hypotheses, a single salvo of at most five sources each on a different angle, then an interpretation of the deviations. Naming the lens is the book's problematique step, and it is what keeps three hypotheses serving one question instead of scattering across three. Every finding must carry Kaynak: <URL> and one of verified, claim or unknown; unlabelled lines are deleted mechanically before synthesis and unknown lines may not be used as support. When there is no evidence the report says so, because absence of evidence is not evidence.
  • Open the research lane in tools/llm.py: run_prompt(web=True) grants WebSearch and WebFetch for that one call. The execution tools stay denied even then, and the fetched text reaches the synthesis pass fenced as untrusted data, so the furthest a poisoned page can reach is a wrong sentence in a report.
  • Add a goose provider to tools/llm.py for local inference on the worker, invoked with --no-profile so it loads no extensions and has no shell. This is the goose-side equivalent of the deny list.
  • Route the model per lane, not per installation. Every call site now names a lane (python3 tools/llm.py --lanes), and BRAINLESS_LLM_PROVIDER_<LANE> points that lane at its own provider, so a note classifier answering with one word out of three can run on hardware you own while a weekly synthesis still goes to a large model. Two reasons, pulling in opposite directions: privacy wants the local model as a lane's primary, resilience wants it as a fallback for the cloud lanes (BRAINLESS_LLM_FALLBACK=goose), and both are the same setting seen from different sides.
  • The fallback only goes one way. Cloud may degrade to local; local never escalates to cloud, because a lane pinned local was pinned for privacy and a timeout is not consent to send the same text somewhere else. BRAINLESS_LLM_ALLOW_CLOUD_FALLBACK=1 says otherwise, explicitly, per machine.
  • Log the routing: .agents/state/llm_log keeps the last 300 calls with lane, provider, outcome and wall time, next to the single latest outcome in llm_status that the health check reads. tools/llm_bench.py times the real prompt shapes against a provider, because whether a local model is viable is a measurement, not an opinion.
  • Guard the task ledger deterministically in spiky_actions.py. The prompt asked the model not to repeat a task already in the ledger, which is a string comparison wearing a prompt; a duplicate is now dropped after the model, with Turkish suffixes folded, so a weaker local model cannot send the same promise to a person twice.
  • State the base rate in the epic rubric: an epic is about one note in twenty. Found by measurement, not by taste. Without it a local 4B model promoted four notes out of eight to epic that Opus called stories, always in that direction, which is the expensive one because an epic triggers a research pass. With it the two models agreed on all eight, and both still called an unmistakable multi-month programme an epic, so the sentence tightened calibration without turning the classifier into a constant.
  • New guide: docs/local-inference.md, including what the worker hardware can actually do, how to seed a model over a slow link, and the order in which lanes should move.
  • Add _Agent-Context/SOURCES.json, a machine-readable source registry. Scripts may only append to candidates; promoting a candidate is the owner's call, at most one new stream per month, and a source that has fed nothing for eight weeks is flagged rather than removed.
  • Add tools/lang_detect.py and answer in the language that was used. Research reports follow the triggering note, and the dialectic engine now argues each topic in the language of the notes behind it instead of forcing English.
  • Reframe both READMEs around connectivity and the three layers (collect, think clearly, decide), including the failure mode the layering guards against, and correct the origin of the name.
0.1.6release notes

Version 0.1.6

  • Add docs/scripts.md: a reference for every Python script in tools/ and .agents/scripts/, each with a definition, a description and the design philosophy behind it, plus a map of how the scripts relate. Linked from the README.
0.1.5release notes

Version 0.1.5

  • Add the Today queue: at most one decision, one commitment, and one evidence review. Telegram supports preview, apply, edit, defer, and dismiss. Repeated deferrals ask for a blocker or smaller step; seven-day completion counts and source links appear in TODAY.md. The existing morning reminder sends the queue.
  • Record approved decision outcomes in calibration and recover interrupted Today writes without duplicating entries. See docs/today-queue.md.
  • Preserve original documents and raw conversions after processing. Failed, missing, or empty AI summaries now trigger retries; generated output is validated before replacing an existing summary.
  • Apply project privacy checks to every descendant source file.
  • Rebuild project mirrors when any permitted source changes, is added, renamed, or removed. Existing mirrors rebuild once under the new dependency policy.
  • Keep kill criteria out of dashboard actions and open loops. Prefer explicit Next Action sections, now included in the project template.
  • Add offline regression tests for document retention, project privacy, dashboard actions, and the Today queue; run them in public CI.
  • Rewrite the README around one loop and add docs/capture-flow.md on the capture flow and the Obsidian network.
  • Keep private finance tooling out of the export. tools/export_public.py now excludes the finance folder, because a fixture of real figures passes any word scan. Deployment-specific agent rules move to a private companion file that _Agent-Context/AGENT-RULES.md points to and the export never lists.
  • Workers pull with --autostash, the cron wrapper commits its regenerated context blocks locally so the tree does not sit dirty, and the content engine reads the editing rules alongside the production guide.
  • VERSION catches up: 0.1.4 shipped with the file still reading 0.1.3.
0.1.4release notes

Version 0.1.4

  • Dialectic scorecard. Round one is isolated (one Buzz root per persona, so nobody reads anyone else before answering); round two is one root that quotes every round one reply. Replies end with Vote: (YES, NO, CONDITIONAL) and Number: (NN%), round two also with New evidence:. score_topic() turns those lines into a deterministic per-topic scorecard (affirmation rate, who moved and whether they cited evidence, a unanimity warning) that is filed with the note, posted to the channel and fed to the synthesis. Rounds append to .agents/state/dialectic_scores.jsonl; the worker writes the rolling 30 day view to _Agent-Context/DIALECTIC-SCORECARD.md with two flags: sycophancy (affirmation above 60 percent) and a persona that never votes NO. brainless dialectic --scorecard prints it. Persona rules and the /dialectic command carry the same headings; redeploy the personas after upgrading.
  • Kill criteria. Every project notes.md may carry a ## Kill Criteria section of - [ ] YYYY-MM-DD | condition | consequence lines (the project template has it). tools/kill_criteria.py scans them deterministically: a past date with an open box is a breach. Breaches turn the new health check row red, are written to _Agent-Context/KILL-CRITERIA.md for the briefing, lead the dashboard's new Kill criteria section and the think surface's provocation; PROJECTS-ACTIVE shows each project's next criterion, and projects without one are listed.
  • Vault topic fallback: on a day with no captures the evening dialectic argues one thing the vault is waiting on (a decision past review, a decided note without a prediction, a pending decision near review, a stale belief, a live question), rotated with a 14 day cooldown, instead of idling.
  • Health check: a "Morning briefing" row goes yellow on weekdays after 08:30 when the day's briefing is missing or lacks the health block.
  • Think surface: the cadence step says how many days it has waited when it exceeds two weeks.
  • Every thinking command prompt now ends with a Loopback step that files its output through file_query.py.
0.1.3release notes

Version 0.1.3

  • The engine is English end to end: every comment, docstring, log line, help string, shell message, prompt and convention document. No Turkish left in code.
  • Output language is a setting, any language: output_lang in PROFILE.md takes any ISO code. LLM prompts carry a language directive; deterministic strings (health labels, bot replies, generated headings, section names) come from tools/locale/<code>/<script>.json through tools/i18n.py, English as the fallback. Shipped locales: en, tr. Add a directory to add a language. See docs/localization.md.
  • Scripts that parse headings they wrote earlier accept both the current language and English, so existing vaults keep working.
  • Buzz identities renamed to English: briefing, thinking, tasks, content (key files under ~/.config/brainless/buzz/keys/), and the owner env file is assistant.env. Existing relays rename the files once.
  • Installer accepts any two or three letter language code.
  • Document conversion calls markitdown as a Python library (tools/markitdown_native.py), not the CLI: one reused converter instance, no PATH discovery, no per file subprocess. smart_processor.py and batch_markitdown.py share it.
  • No em or en dashes anywhere in the tree; the leak scan and CI stay as before.
0.1.2release notes

Version 0.1.2

  • CRM snapshot addon: read-only pull from Pipedrive into _Agent-Context/CRM.md and per-organisation event logs under Inbox/CRM/. Organisation and deal level only, no person data, no LLM. Provider seam for other CRMs. Systemd timer for the worker, hourly run on the laptop. Documented with a Pipedrive example in docs/addons/crm-pipedrive.md.
  • Health check row for the CRM snapshot, and an optional Buzz #crm channel (install_crm_channel.sh, buzz_crm_sync.sh) that receives the block when it changes.
  • buzz_post.sh no longer needs coreutils timeout on macOS.
  • Flag severity: an organisation without an open deal is never red; red is reserved for deals.
  • VERSION file and brainless version.
  • docs/ ships with the export.
0.1.1release notes

Not tagged at the time; tagged retroactively on the last commit of the day.

  • One-line installer (install.sh) and the brainless command.
  • README rewritten around the decision loop and the five voices, with the real three-device setup as a usage example.
  • Calibration (brainless calibrate) and the think-surface command.
  • Transcript filter, dialectic trigger, failure alerts on the worker.
  • CI leak-scan gate: every push and pull request runs the export scan.
0.1.0release notes

Version 0.1.0

  • First public export: engine, commands, personas, installer, MIT license.