Skip to content

Changelog

All notable changes to Brain Framework (formerly FKF) are documented here. This project follows Semantic Versioning from its first public release.

Unreleased

v21.1.0 - 2026-11-04

A minor release. The brain format (version: 8), the reply schemas, the queue format and the search cache are unchanged; bf.yaml accepts one more form, so no upgrade step is needed.

Added

  • Retries by run kind. An assistant's idempotent accepts a list of run kinds besides true and false, such as [schedule, wake]: a failed or interrupted attempt of a listed kind retries by itself with backoff, while other runs, such as an answer to a message that may have acted, wait for bf queue ID --retry or --cancel (what a run records).

v21.0.1 - 2026-11-04

A patch release. The brain format (version: 8), bf.yaml, the reply schemas, the queue format and the search cache are unchanged; no upgrade step is needed, except to recover a queue that a device renumbering already stranded (below).

Fixed

  • A new device number no longer starts an empty assistant queue. BF names a brain's queue and program-state folders by its device and inode numbers, and btrfs can number a device anew at boot: the queue then looked empty, stranding unresolved work, and window sensors restarted from lookback. When the folder for the brain's current identity is missing, BF now renames the folder with the same inode whose queue is bound to the brain's path, together with its program state; a brain without a queue still restarts its window sensors from lookback. Several matching folders, or one that cannot be read, such as after an interrupted write, make assistant commands fail with several assistant queues in the state folder runtime/ are bound to this brain (…) or assistant queue in the state folder runtime/ (NAMES) may belong to this brain but cannot be read instead of choosing; collection and watch keep running (move or rename a brain).
  • Recovering an already stranded queue (manual). A brain that already ran after a renumbering has a new, possibly empty folder, which this release keeps. To recover the old queue: stop bf watch, check that bf queue shows no work you need, move runtime/NEW (named by stat -L -c %d-%i BRAIN, or stat -L -f %d-%i BRAIN on macOS) out of the state folder, rename the old runtime/OLD-INODE folder to NEW (and programs/OLD-INODE likewise), then run bf queue.

v21.0.0 - 2026-11-04

A major release that makes assistants event-driven and lets them choose when to speak. A structured reply replaces answer pointers and status checks, a gate decides whether a scheduled run needs the model, an assistant can schedule its own next run, and one post channel replaces output routing. The brain format (version: 8), records, notes, the search cache and the queue format are unchanged; bf.yaml assistants need manual edits. Follow the upgrade steps below.

Breaking changes

  • reply replaces text and status. An assistant with reply: {path: POINTER} reads a structured {say, wake} object from its harness's JSON stdout, such as /structured_output, and {{schema}} passes the harness that schema. Output that is not a JSON object, a missing reply or one that does not match the schema fails the attempt like a nonzero exit, which also catches a harness that exits 0 after a model error or its own timeout. null or blank say stays silent, except in answer to a message, where it fails as an empty answer did. bf validate reports assistants.NAME.text: Extra inputs are not permitted and the same for status (structured replies). Without reply, stdout is still the plain message.
  • post replaces outputs. A message is answered in its own conversation, as reply: {} did, and every other run, manual ones included, posts to the one channel post names. outputs, reply: {} and when are gone: bf validate reports assistants.NAME.outputs: Extra inputs are not permitted. One answer makes at most one delivery; to notify a second channel, declare a second assistant or let the channel's script fan out.
  • Input channels need send. Every channel an assistant reads must declare send, so its messages can be answered: assistants.NAME.inputs: channel X requires send to answer its messages.
  • Runs of kind wake. BF_TRIGGER, {{context}}'s kind, a record's attributes.trigger and bf update --dry-run rows may now be wake, and records may carry attributes.wake. Scripts that match the run kinds need the new value.

Added

  • Gates. A schedule's optional gate: {command: [...]} runs a deterministic program before the harness. It prints {"events": [...]}, at most 64 events and 64 KiB; no event completes the job without starting the model, and events reach the prompt in {{context}}. A failed gate retries by itself, since no harness ran, and a retried job reuses the events its gate saved, so a gate can keep "already reported" state in BF_STATE_DIR (gates).
  • Wakes. With wake: {min, max}, a structured reply's wake: {in, reason} schedules one later run, clamped to those bounds. A newer request replaces the pending one, a failed run asks for none, and the wake run receives its reason in {{context}}. bf queue lists wakes by assistant, due time and the job running it (wakes).
  • bf schema --kind assistant-reply prints the reply schema, for a harness that reads it from a file, such as codex exec --output-schema FILE; it is published as assistant-reply.schema.json.

Changed

  • Literal }} in arguments. A command argument may hold }}, such as the end of inline JSON settings; only an unknown {{…}} placeholder fails. Before, }} had to be written } }.
  • Assistant safety. Keep prompts safe now states that an assistant with a shell or write tools can be steered by the mail, chat or web content it reads, whatever BF labels as data.
  • bf validate no longer checks an assistant's queue space per answer: with one delivery per answer and the 4 MiB max_bytes bound, every answer fits.

Fixed

  • The assistant guide stated that an idempotent assistant retries up to 5 attempts after 5 × 2^(n−1) seconds; it retries after 2, 4, … up to 300 seconds, for up to 20 attempts, as the code always did.

Upgrade from v20

  1. With v20 still installed, stop bf watch, its service or timers and any script calling bf run, bf channel or bf queue.
  2. Drain the assistant queue: run bf update --kind assistant, then retry or cancel each pending, failed or uncertain job until bf queue counts show only complete and cancelled. A job v20 queued snapshots its old settings, which v21 cannot run: after upgrading, cancel any you missed with bf queue ID --cancel.
  3. Upgrade with uv tool upgrade brain-framework, or update a pinned brain's pin, and check bf --version.
  4. In bf.yaml, for each assistant:
  5. Replace outputs with post: CHANNEL, naming the channel its scheduled runs posted to, and delete reply: {} and when. Manual runs now post there too.
  6. Add send to each channel listed in inputs that lacks it.
  7. Either switch to a structured reply: pass the schema, such as --json-schema "{{schema}}" for Claude Code or Antigravity, set reply: {path: /structured_output} and delete text and status; or keep a plain answer: delete text and status and make the harness print plain text, since its whole stdout becomes the message.
  8. Replace } } workarounds in arguments with }} if you like; both work.
  9. With a structured reply, rewrite each prompt's answer section: fill say, or null when nothing needs the owner. Delete rules such as "answer exactly -", answer marker lines and wrapper scripts that strip harness chatter. Move deduplication and threshold rules into a schedule's gate, and add wake: {min, max} where the assistant should schedule its own follow-up.
  10. Update scripts and prompts that match BF_TRIGGER, attributes.trigger or {{context}}'s kind for the new wake kind.
  11. Run bf validate, bf skills DIR for each skills folder, bf run NAME once per assistant and bf status --check, then restart watch.

v20.1.0 - 2026-11-01

A feature release for integrations and evidence hygiene. The brain format (version: 8), existing bf.yaml files, the search and read reply schemas and the search cache are unchanged. Upgrade steps: run bf skills DIR to update the packaged skills, and check that no sensor relies on the silent cut of record text above 4 MiB fixed below: set truncate_text: true on such a sensor. A new brain from bf init declares the standard vocabulary; an existing brain keeps its fields.

Added

  • Skill behavior eval. mise run eval:skills runs five cases in headless Claude Code against a fresh copy of the example brain, isolated from user settings and provider tools, and grades the skill used, the cited ref, forbidden commands and written files. It needs a Claude login and spends tokens, so it stays out of mise run all (contributing).
  • Sensor kit and standard vocabulary. examples/sensors/_sensor.py is a standard-library helper file to copy next to your sensors: window and state from the environment, bounded provider CLI calls and HTTPS fetches, identities, credential masking and records within BF's bounds; github-issues.py shows a sensor built on it. bf init now declares the standard vocabulary with default paths, so a sensor printing author_refs, sender_refs, status and the like needs no fields: mapping in a new brain. The Gmail example prints sender_refs and recipient_refs instead of from_refs and to_refs, and the GitHub history example status instead of state: update a mapping copied from them.
  • Check saved sensor output. bf collect SENSOR --input FILE (or - for stdin) validates and projects saved output with the brain's current mappings without running the sensor or saving anything, even for a disabled sensor: a contract test for sensors (check saved output).
  • Program environment. Sensors and routines receive BF_PROGRAM, their window as BF_START and BF_END, and BF_STATE_DIR, a private directory on this machine for cursors and tokens kept between runs (what a program receives).
  • Forget and expire records. bf forget REF... removes collected records and lists their digests in memories/SOURCE/.forgotten, so collection never stores them again; a sensor's expire_after removes records older than its retention and skips older ones it prints again. Runs report forgotten and expired counts (keep, forget and expire records).
  • Retired sources. A sensor or routine with enabled: false may omit command, so a retired source keeps its records searchable without a placeholder command.
  • Period plus scope. A search scope can combine a period with one other scope, such as --scope "7d repo:github.com/owner/name" for the records naming a repository this week (scopes).
  • Text export. bf export --kind items prints every note and record with its metadata, fields and text as JSON Lines, for retrievers outside BF such as embedding or multilingual search.
  • Assistant trigger and harness status. A harness learns why it runs from BF_TRIGGER (input, schedule or manual), BF_SCHEDULE and its BF_START/BF_END window, so a wrapper no longer parses the prompt. The optional status: {path, success} setting turns a failure the harness reports in its JSON output while exiting 0, such as a model API error, into a failed attempt with the usual uncertain or idempotent retry rules; the error names at most a one-word status, never the output (assistants).

Fixed

  • Oversized record text. A record whose text exceeds 4,194,304 characters now fails its collection, as documented, unless the sensor sets truncate_text or max_text_bytes; it was cut silently. The sensor settings now state the actual 4 MiB default and 1–4,194,304 byte range of max_text_bytes.

Changed

  • Usage counts. bf status usage covers the full 7 and 30 days on busy brains: rotation sums each past UTC day into one line instead of dropping the oldest events; since remains only while a cut made by an earlier release falls inside a window. Each window adds kinds, the searches by scope kind and reads by page kind, and empty_kinds, the scope kinds of zero-result searches; the log still keeps no query text, refs or paths (limits). Windows now count today and the 6 or 29 UTC days before it, instead of the last 168 or 720 hours.
  • Packaged skills. bf-use and bf-action check the bf version once per session instead of before every task, and ask bf status about a pinned runtime only in a brain with a uv.lock. Helpers run by their literal path (python3 SKILL_DIR/scripts/NAME.py with the absolute folder) and take multi-line text from files, because hosts with strict permissions refused the $SKILL_DIR variable and $'…' quoting, which blocked saving a decision and starting an action. bf-action resumes only an action whose folder topic matches, not one that mentions the subject, and bf-maintain's description leaves choosing sources to bf-setup. Run bf skills DIR after upgrading.
  • README. About half as long, with a quickstart after the first figure and each point made once; details moved to the linked documentation.
  • Documentation. Every page reads faster with the same content: guides open with the main path, edge cases sit in reference sections or collapsed blocks, and dense paragraphs became tables and lists. Sentences over 40 words dropped from 84 to 30. Page paths, heading anchors and examples are unchanged.
  • Major releases. A breaking change ships in a major release whenever the design needs it, instead of waiting for a quarterly window; related breaks still batch where practical (Contributing). AGENTS.md is about half as long and names the current scope: no model inference, provider SDKs or record sync in the core, integration helpers as a copy-in kit, and assistants, channels, watch and multi-brain retrieval open to improvements backed by real use.
  • Release tooling. scripts/release.py prepares a release in a clean clone that ships only the named paths, runs the gate, publishes after CI and CD, verifies the artifacts and syncs the primary checkout only when no other session changed a released path. The release checklist keeps only the judgment steps.

v20.0.2 - 2026-10-31

A maintenance release for repository tooling and contributor checks. The package behavior, the brain format (version: 8), bf.yaml, the reply schemas, the packaged skills, the assistant queue and the search cache are unchanged; no upgrade step is needed.

Changed

  • Repository checks and workflow tools. Secret scanning uses Betterleaks 1.9.0 (.betterleaks.toml), workflow linting uses jactionlint 2.4.0 and zizmor 1.31.1 with $/ self-repository reusable workflow references, and dprint's Markdown plugin is updated to 0.28.0.
  • Linter, hook and task hardening. pyproject.toml adds Ruff's BLE, PLE and PLW rule families and rejects blanket ty ignore comments; lefthook.yml uses sequential jobs; mise.toml enforces min_version = "2026.10.21" and UV_MALWARE_CHECK=1 on install and upgrade; and AGENTS.md and bf-contribute format rules with scannable lead-ins.

v20.0.1 - 2026-10-31

A documentation release that aligns the guides and packaged skills with v20. The brain format (version: 8), bf.yaml, the reply schemas, the assistant queue and the search cache are unchanged. Upgrade with uv tool upgrade brain-framework (or update a brain's pin), run bf skills DIR for each skills folder, and compare a brain's AGENTS.md with a scratch bf init copy to adopt the starter's new first line.

Fixed

  • Guides match v20. Checks describes the validate: policy shapes bf validate accepts: index_note names one index note, required_status lists statuses and forbid_patterns holds up to 64 regular expressions. {{lines:FILE}} arguments document their real bounds: a file of at most 4 MiB, lines of at most 16,384 characters and at most 128 command items in all. Pages no longer mention the removed bf schedule or queued trigger events, and the queue folder command follows a symlinked brain (stat -L).
  • Upgrades and recovery. Install and update runs the steps that need the previous release, such as draining the assistant queue, before upgrading, and ends with bf status --check; its libyaml reinstall names 'brain-framework[mcp]' so MCP users keep the extra. Troubleshooting no longer sends unexpected assistant queue schema to the v19 steps.
  • Packaged skills. bf-maintain follows the same upgrade order, installs the mcp extra when a host starts bf mcp, updates the watch service or timer you wrote instead of regenerating schedules and explains assistant queue predates this release. bf-use and the starter AGENTS.md that bf init writes search the brain before querying a source tool. bf-use, bf-action and bf-setup point agents to the Markdown documentation at llms.txt, and bf-setup warns that a CLAUDE.md in a parent folder also replaces AGENTS.md.

v20.0.0 - 2026-10-31

A major release that trims BF's surface and settles deferred input rules. bf schedule and assistant triggers are removed, MCP becomes an optional extra (13 runtime packages instead of 36 on Linux and macOS), disabled programs stop reporting failures, and six ambiguous inputs that loaded before now fail with a message naming the fix. The brain format (version: 8), records and notes are unchanged; the assistant queue needs one manual SQL statement and the search cache rebuilds once. From this release on, breaking changes ship together in at most one major release per quarter. Follow the upgrade steps below.

Breaking changes

  • bf schedule is removed. BF no longer writes systemd, launchd or cron files. To run unattended, keep bf watch --json --brain BRAIN running as a user service you write yourself, as Watch and schedule updates shows for systemd and launchd. A timer that runs bf update --brain BRAIN also still works.
  • MCP is an optional extra. uv tool install brain-framework no longer installs the MCP SDK, which cuts the base install from 36 to 13 runtime packages on Linux and macOS. Without the extra, bf mcp exits 1 and names uv tool install --python 3.14 'brain-framework[mcp]'. uv tool upgrade keeps the extra once it is installed.
  • Assistant triggers are removed. triggers, the sensor.changed event and the trigger kind of run are gone: bf validate reports assistants.NAME.triggers: Extra inputs are not permitted and rejects trigger in an output's when. Sensor changes start no assistant and record writes never touch the queue, so a collection can no longer fail after saving with SOURCE records were committed but their assistant event is not queued.
  • Queue format 2. The assistant queue drops its triggers table. Execution and bf status refuse a v19 queue with assistant queue predates this release; preserve it and follow the upgrade steps in the changelog.
  • Assistant replies and runs. bf queue and status messaging drop triggers and trigger_counts, and bf update rows no longer name a trigger. A record's attributes.trigger is input, schedule or manual. Neither records nor {{context}} carry event, and harnesses no longer receive BF_ASSISTANT_RUN.
  • No program is named bf. A sensor, routine, channel or assistant named bf stops bf.yaml from loading with sensors.bf: the name bf is reserved for BF addresses; rename the sensor and move memories/bf/ to its new name, because its records' bf:ID refs read as BF addresses. Records left in memories/bf/ are skipped and reported until moved.
  • A BF address ending in a bare # is invalid. bf read, bf search and the MCP tools reject bf://brain/projects/plan.md# as invalid input (exit 2), as they have rejected a plain ref ending in # since 18.5.0; before, it read and scoped the whole note. A note writing one in a link, frontmatter links, sources, entity or aliases is invalid and unsearchable, and a record holding one in its links or aliases is skipped and reported. A relative link such as plan.md# still links the whole file.
  • Search scopes take no section. --scope bf://brain/projects/plan.md#decision and --scope memories/SOURCE#x are invalid input (exit 2) in the CLI, the MCP search tool and eval suites, like a section's path. A suite holding one fails to load and names the field, such as cases.1.scope.
  • bf init --name "" is invalid. An empty or blank name exits 2 and creates nothing. Omit --name to derive the name from the folder.
  • libyaml is required. Without it, every command that reads YAML exits 1 and names the fix, uv tool install --reinstall --python 3.14 brain-framework. BF used to fall back to PyYAML's slower pure-Python parser, which rejects some YAML that libyaml accepts.
  • OKF sources resources hold at most 8,192 characters, like frontmatter links. A longer one fails as invalid frontmatter: sources: reference exceeds 8192 characters.

Changed

  • Disabled programs report no failure. A disabled sensor, routine or assistant shows no failed, error, failures or log in status, coverage or watch, adds no attention entry and never fails bf status --check. Its unresolved jobs stay in bf queue and messaging, and count again once re-enabled; a removed assistant's jobs still fail the check. This reverses v19's "even once disabled".
  • One breaking window per quarter. Breaking changes now wait for one batched major release, at most once a quarter, and a new rule that rejects input loading today warns first where it can (Contributing). The bf watch terminal dashboard is frozen: it gets fixes only, and new run information goes to bf watch --json and bf status.
  • The search cache rebuilds once on first search or read.

Upgrade from v19

  1. With v19 still installed, stop bf watch, its service or timers and any script calling bf run, bf channel or bf queue, including jobs bf schedule installed: they run bf update and keep working after the upgrade, so re-enable them at the end or replace them with a watch service. Already upgraded? Reinstall the previous release with uv tool install --python 3.14 'brain-framework==19.1.0', do steps 1 and 2, then upgrade again: v20 cannot drain a v19 queue or recover a v19 memories/.pending journal.
  2. Drain the assistant queue: run bf update --kind assistant, then retry or cancel each pending, failed or uncertain job, including those of disabled assistants, until bf queue counts show only complete and cancelled; deliveries may stay. If memories/.pending exists, run v19's bf build.
  3. Back up the brain and the state directory's runtime/ folder, then upgrade with uv tool upgrade brain-framework, or update a pinned brain's pin, and check bf --version. If an agent host starts bf mcp, install uv tool install --python 3.14 'brain-framework[mcp]' instead and check that uv tool list --show-extras shows [extras: mcp]. If a command reports reading YAML needs PyYAML built with libyaml, run uv tool install --reinstall --python 3.14 brain-framework, or 'brain-framework[mcp]' when you use MCP: a reinstall without the extra drops it.
  4. Update the queue in place; never delete it. It lives under $XDG_STATE_HOME/bf/runtime/ (normally ~/.local/state/bf/runtime/), in the folder stat -L -c %d-%i ~/brain names (stat -L -f %d-%i ~/brain on macOS). Skip this step when that folder holds no queue.sqlite3: running sqlite3 would create an empty file BF then refuses.
queue=~/.local/state/bf/runtime/"$(stat -L -c %d-%i ~/brain)"/queue.sqlite3
[ -s "$queue" ] && sqlite3 -bail "$queue" "BEGIN; CREATE TEMP TABLE drained(n INTEGER CHECK (n = 0)); INSERT INTO drained SELECT count(*) FROM jobs WHERE state NOT IN ('complete','cancelled'); DROP TABLE triggers; PRAGMA user_version=2; COMMIT;"

The statement refuses a queue that still holds unresolved jobs, and a rerun exits 1; both change nothing. Python's sqlite3 module can run the same statements. 1. In bf.yaml, delete triggers: and trigger from each output's when. Give a trigger-only assistant a schedule, such as schedules: {review: {every: 3600}}, with a prompt that reads bf read memories/SENSOR/24h. Rename any program named bf, also in the inputs, outputs and --sensor, --routine or --assistant selectors that name it, and move its records with mv memories/bf memories/NEW, renaming any memories/bf exception in .gitignore; its log and run history start again under the new name. 1. Run bf validate: it names each note holding a BF address that ends in a bare # or an OKF sources resource over 8,192 characters, and each record whose links or aliases hold such an address. Remove each # or name the section, shorten the resource, and fix the sensor that printed the record before collecting it again. 1. In scripts, host configuration and evals/*.yaml, scope the whole note and expect the section, drop the #… from memories/SOURCE#… scopes and set or omit bf init --name. Update prompts and scripts that read event, attributes.trigger == "trigger", messaging.triggers, trigger_counts or BF_ASSISTANT_RUN. 1. To run unattended, re-enable the jobs you stopped in step 1, or replace them with the user service from Run watch as a service. Never run both on one brain. 1. Run bf skills DIR for each skills folder, then bf validate, bf eval and bf status --check, and restart watch.

v19.1.0 - 2026-10-29

A feature and fix release. The brain format (version: 8) and the reply schemas are unchanged; the search cache rebuilds once on first search or read. Upgrade with uv tool upgrade brain-framework (or update a brain's pin) and run bf skills DIR to refresh installed skills.

Added

  • Assistant schedules accept wall-clock at: "HH:MM" times with optional IANA timezone and days (mon–sun) alongside interval every schedules, pass context.schedule in {{context}} for scheduled runs, and treat an empty scheduled or triggered reply without output metadata as a silent no-op that advances the schedule without writing an empty record.
  • Assistants with read-only harnesses can set idempotent: true to automatically retry pre-delivery harness failures and interruptions up to 5 attempts with exponential backoff (5s to 300s), and bf queue ID --retry resets the retry budget.
  • Top-level fields.<name> declarations in bf.yaml accept an optional default JSON Pointer path, used when a sensor does not specify an override in sensors.<sensor>.fields.<name>.
  • Sensor, routine, assistant and channel command arguments support {{lines:relative/path.txt}}, expanding non-blank, non-# lines from a brain-relative UTF-8 file (up to 64 KiB and 500 lines) into separate arguments and checking the file during bf validate.
  • Sensors can set truncate_text: true (and optional max_text_bytes up to 1 MiB) to truncate oversized record.text on a valid UTF-8 codepoint boundary, stamp attributes.truncated: true and log the record ID instead of failing the whole batch.
  • bf.yaml accepts a top-level validate: policy (max_stale_days, warn_stale_days, index_note, required_status and forbid_patterns) enforced by bf validate.
  • bf update runs up to four due sensors concurrently while returning windows in configuration order.
  • Section search passages include the note's filename stem (when distinct from its H1 title) and declared aliases in their lexical context, so multi-word queries combining a note's stem or alias with a section's body terms match that section directly.
  • Zero-result searches record up to 8 unmatched query tokens (up to 64 characters each) in local usage.jsonl, and bf status surfaces the top 10 recurring empty_terms in each usage window.

Fixed

  • A channel input no longer stops when a provider edits a message it already admitted: the edit keeps the admitted version and starts no job, and the messages after it are admitted. Before, every poll failed with a channel reused an event id with different content until the edited event expired 35 days later.
  • Editing an assistant's schedules, triggers, inputs or keep no longer fails its queued jobs with assistant configuration changed, and once a harness has answered, only removing or disabling its assistant or changing an output channel stops its saved answer from being recorded.
  • A harness or channel send command that never started, such as one not on PATH under a timer's shorter PATH, leaves its job failed instead of uncertain, and its delivery retries like an idempotent one instead of waiting as uncertain: nothing ran, so retrying is safe.
  • An unresolved bf run no longer holds back the saved answer of a later bf run of the same assistant: each manual run is its own slot.
  • With several brains selected, a read's outline refs and a claim's target_ref are bf:// addresses, like sections, so reading them no longer fails with reference exists in several brains.
  • A sensor or routine run that waited 120 seconds for a busy brain no longer waits another 120 seconds to record that failure.
  • bf register leaves the registry file, and its inline comments, unchanged when the brain is already registered at the same path.
  • bf channel rejects invalid arguments with exit code 2 before selecting a brain, as every other command does, and bf skills reports its destination as an absolute path.
  • The example Calendar, Drive, Gmail, Git and GitHub sensors ignore a Git hook's repository variables, such as GIT_DIR: started from a hook, the Git history sensor saved the hook's repository commits under every repository's name.
  • Search, the retrieval example and the link reference say that tags weigh more than body text and less than headings, as ranking does.

Changed

  • bf-use's guarded-write helper replaces a whole note only from --new-file FILE given alone and never reads standard input, which an agent's shell may hold open; its guide writes --old=TEXT --new=TEXT so a passage starting with - stays an argument. Run bf skills DIR to update installed skills.
  • The Assistants guide's complete example runs Claude Code with the prompt on standard input, as the page recommends for private messages, and Core concepts lists channels among a brain's programs.
  • Development tools: uv 0.13.6, dprint 0.64.3 with the Markdown 0.27.1 plugin, trivy 0.76.1, ruff 0.17.3, ty 0.0.94, pytest 9.2.0, zensical 0.0.73, and the workflows pin mise 2026.10.21, jdx/mise-action v5.2.4 and pypa/gh-action-pypi-publish v1.14.3. The locked environment moves to starlette 1.10.0, sse-starlette 3.6.0, httpx2 and httpcore2 2.16.0 and pycparser 3.12.

v19.0.1 - 2026-10-20

A fix release. The brain format (version: 8), bf.yaml, the reply schemas and the search cache are unchanged. Upgrade with uv tool upgrade brain-framework (or update a brain's pin); bf skills DIR refreshes the bf-maintain operations guide, whose only change is naming assistants where it still said prompts.

Fixed

  • bf update and bf build --reproject no longer wait for the brain's exclusive writer lock when the search cache is current: they check the cache's pages (PRAGMA quick_check(1), with a 64 MiB page cache) and compare file fingerprints under the shared reader lock, so a refresh with nothing to index runs beside searches and long reads and commits nothing. A refresh that finds changes still takes the writer lock and checks the pages again if another refresh replaced or changed the cache meanwhile.
  • The privacy page, the bf-maintain operations guide and the example prompts/.gitignore name an assistant's harness and runs where they still said prompt, as 19.0.0 renamed them.

v19.0.0 - 2026-10-17

A major release. Assistants replace prompts: one harness answers messages from brain-owned channel commands in their thread, reviews on named schedules or after a sensor changes and fans its saved answer out to several channels, while a durable local queue keeps every accepted message, answer and delivery. The brain format moves to version: 8: bf.yaml renames prompts: to assistants:, replaces their refresh and lookback with named schedules and adds channels:. Search and read replies name an assistant's attention entry assistant instead of prompt and gain the optional silent flag that home attention entries already carried, and the search cache rebuilds once. Follow the upgrade steps below before collecting again; they include this release's skill, example, AGENTS.md and Claude Code command updates.

Breaking changes

  • Assistants replace prompts. prompts: becomes assistants:, and refresh and lookback fail validation. There is no default schedule: an assistant runs on its own only through schedules, inputs or triggers, so a v18 prompt without refresh, which ran every 30 minutes, runs only with bf run until you give it a schedule.
  • Failed runs wait for you. A harness run that fails, times out or is interrupted, whether a schedule, a message or bf run started it, stays failed or uncertain in bf queue, blocks the later work of its schedule or conversation and makes bf status --check exit 1 until bf queue ID --retry or --cancel resolves it. A run killed outright, as by the out-of-memory killer, shows running until a running watch records it within about a minute, the next bf update does or bf queue ID --retry or --cancel names it. v18 retried a failed scheduled prompt after a backoff and only logged a failed direct run.
  • Evidence windows. {{start}} and {{end}} depend on the kind of run: a schedule covers the time since its own last run, a message the moments between its sending and its admission, and bf run the 24 hours before it. In v18, a direct run, such as a chat bridge's, also covered everything since the last scheduled run.
  • BF delivers answers. A run's answer goes to the assistant's outputs; bf run sends it to every output without when or with manual in it. A harness or bridge that still posts the answer itself sends it twice.
  • Run records. attributes.trigger names the kind of run, input, schedule, trigger or manual, instead of schedule or run; elapsed_seconds is gone; job and, for a message, channel, sender and conversation are added; a record's id and time follow its window's end instead of the run's start.
  • Commands and replies. --assistant replaces --prompt on update, watch and schedule, and --kind assistant replaces --kind prompt. run and update replies list assistants entries named by assistant, home and status attention entries name assistant, and bf watch --json reports assisting instead of prompting. The update reply's manual names enabled assistants without schedules, inputs or triggers, which only bf run starts, instead of prompts with refresh: 0. A native schedule generated with --prompt exits 2 until you regenerate it.
  • Status. An assistant's status entry drops last_run and failures, adds schedules and inputs, reports manual freshness without a schedule and is failed while one of its jobs is failed or uncertain, as is its source in sources and in search and read coverage, even once disabled. attention also names an assistant with failed or uncertain jobs, whatever its schedules, as on the home page; status alone also names an assistant whose schedules or triggers cannot start, and a channel with an unresolved delivery that failed or is uncertain or a stream or read error, as {"channel":NAME,"freshness":"unknown","failed":true}. A brain with an assistant queue gains messaging, which holds the details; healthy follows problems, attention and the cache alone.
  • Run history. Assistant run counters live in assistants.json in the local state directory: runs restarts at 0, and v18's prompts.json is no longer read.

Upgrade from v18

  1. Before upgrading, note what each prompt does under v18: its refresh (1800 when omitted, 0 for manual runs only) and lookback in bf.yaml, its last_success in bf status, and where its answers go, such as a harness told to post them through a chat CLI or MCP tool, or a bridge that pipes messages into bf run PROMPT --stdin and posts the reply's text.
  2. Stop every execution owner: bf watch, the native schedule jobs (with the remove commands bf schedule returned) and any external chat bridge or listener. Let a bridge finish the messages it accepted, or note them to send again, then stop its service.
  3. Back up the idle brain and its local state directory, $XDG_STATE_HOME/bf/ (normally ~/.local/state/bf/), which holds run history and, from v19 on, the durable assistant queue under runtime/. If you ran a pre-release v19 build on this brain, first resolve with that build every pending, failed or uncertain job and delivery its bf queue lists, using bf queue ID --retry or bf queue ID --cancel, then move its queue.sqlite3 out of the state directory: it is in the runtime/ folder that stat -c %d-%i ~/brain names (stat -f %d-%i ~/brain on macOS), and this release refuses a queue a pre-release build created with unexpected assistant queue schema; preserve it and repair it.
  4. Upgrade with uv tool upgrade brain-framework, or update a pinned brain's pin to the published v19 release, and check bf --version.
  5. Change version: 7 to version: 8 in bf.yaml and every evals/*.yaml. Records and notes keep their format.
  6. Rename prompts: to assistants:, keeping each entry's prompt, command, text, enabled, timeout, max_bytes and keep. A Claude Code command that came from the 18.5.0 prompt guide or bf-maintain's example becomes [claude, --print, --output-format, json, --tools, "Read,Grep,Glob,Bash", --allowedTools, "Bash(bf read *),Bash(bf search *)", --permission-mode, dontAsk], keeping any options you added.
  7. Give each prompt that ran on its own a schedule: replace refresh: N with schedules: {review: {every: N}}, with every: 1800 where refresh was omitted, and move its lookback: L into that schedule, as schedules: {review: {every: N, lookback: L}}. Delete refresh: 0 and its lookback without adding a schedule: bf run now always covers the 24 hours before it, so give such an assistant a schedule if it needs a longer window. A schedule's lookback, 86400 by default, only sets its first window, and v19 does not carry v18's progress over: to keep the first review from covering again what v18 reviewed, set it to just over the time from the last_success you noted to the first v19 run, such as lookback: 7200 for a first run two hours after it. Every run calls your harness's model, so keep v18's interval unless you accept the cost of more runs: every: 1800 makes about 1,440 a month.
  8. Review each prompt file: a message or bf run no longer covers the time since the last review, so wording such as This run covers {{start}} to {{end}} describes moments or the past day there. Keep window wording for schedules and triggers, and let message answers read what they need, such as bf read memories/NAME/24h; Assistants gives each window.
  9. Replace model-owned sends with outputs. Declare each destination as a channel whose send command is a script under channels/ holding the provider access, then list it under the assistant's outputs, or add reply: {} to answer incoming messages in their thread. Remove the posting instructions from the prompt and the posting tool from the harness's allowed commands, such as its --allowedTools: BF delivers the saved answer, and a harness that still posts sends it twice. Declare idempotent: true only when the provider drops a repeated delivery ID.
  10. Replace an external chat bridge with a channel stream or read command and an assistant inputs entry, reusing its provider access and sender admission, and never run the old listener beside the new stream on one provider subscription. Review each adapter's sender admission and destination restrictions, as Channels describes.
  11. Update what consumes BF: replace --prompt with --assistant and --kind prompt with --kind assistant in scripts and host configuration, read assistants, assistant and assisting from replies, filter records on the new trigger values, accept channel entries in status attention and read messaging for the queue's details. Regenerate native schedules with bf schedule, naming --assistant NAME where you named --prompt NAME; a job of their own, such as bf schedule --assistant NAME --name assistants, keeps a long run from delaying collection. The remove commands delete only the installed copies: before regenerating a job under the same --name and --output, delete its old generated files there (normally ~/.config/bf-schedules/bf-BRAIN-HASH-NAME.*), or bf schedule refuses with FILE already exists with different content.
  12. Run bf skills DIR for each skills folder to update bf-use, bf-action and bf-maintain, and copy the updated example weekly-review routine, session-context and prompt-context hooks and git-history, github-history, gmail-headers, google-calendar and google-drive-folders sensors from the release tag, preserving local adaptations: the routine and the session hook read assistant attention entries.
  13. Merge the new AGENTS.md lines into each brain, as Refresh brain instructions shows: they name the channels/ folder, and bf channel and delivery bf queue ID --retry among the commands that need explicit authority.
  14. Check before restarting: run bf validate, bf eval and bf update --dry-run, which lists each due schedule and its window. To see what a harness receives, dry-run a temporary [cat] assistant, as troubleshooting shows. Try each new channel in a disposable copy of the brain, which gets a queue of its own, as the messaging example does. Then start bf watch, which streams need, or install the regenerated schedules, and check bf status --check.

Added

  • Optional send, bounded read and persistent stream channel commands, each with an argv contract and no provider dependencies. Stream acknowledgements follow durable admission for every configured subscriber; execution remains selected. A stream resumes from the cursor of the last admitted message, while its since stays its first start, and clears its transport error once it stays up 5 seconds, though not a refused admission; a read input catches up on a window holding more than 1,000 messages in parts.
  • Assistant fan-in, fan-out, threaded reply: {}, named schedules and sensor.changed triggers. Inputs and outputs live at the same level; channels own transport, assistants own calling policy, and the owner chooses the harness. A sensor change records only the names of the triggers naming its sensor, and watch or bf update admits their jobs from the current bf.yaml: a full queue, a missing prompt or a bf.yaml edit never blocks collection, a trigger that cannot start yet stays pending with its reason, and one whose assistant or trigger was removed or changed is dropped. A native schedule naming a trigger-only assistant admits its triggers.
  • Durable local work and delivery queues outside .bf/, with saved answers, stable delivery IDs, explicit retry/cancel, per-conversation ordering, bounded retention and backpressure. Each commit is durable when it returns; a job reserves its answer space only when it starts, so admission holds hundreds of jobs and a full read batch, and a batch that does not fit admits its longest prefix; identity tombstones and send receipts last 35 days. A drained queue follows a renamed brain or one moved within its filesystem, and search, read and status open it read-only. bf queue inspects, retries and cancels jobs, deliveries and sensor triggers, and a triggered job has an ID of its own; bf channel read|send uses an endpoint directly.
  • Interrupted work is recorded even without watch: Ctrl-C or SIGTERM records a harness attempt or send before bf exits, and bf queue ID --retry or --cancel records one killed outright, refusing while its process still runs. bf status names a queue write a killed process interrupted, which bf queue rolls back, and a queue another build created. bf validate reports an assistant whose answer needs more queue space than one job may reserve, counting each delivery it makes.
  • Watch supervises streams beside collection, with reserved reactive and proactive capacity and up to four independent channel deliveries. Assistant work that cannot start never stops it: watch shows why, including a failing read, stream or delivery, and checks again about once a second, while busy work and a failing assistant's schedules and triggers wait 10 seconds between attempts; it stops only when a stream worker does. bf update reports each failing read, schedule, trigger, job or delivery as its own row while the other assistants run. Failed or interrupted harness attempts require explicit review; only declared idempotent assistant deliveries retry automatically, after 2 to 300 seconds for up to 20 attempts, and a failed bf channel send waits for bf queue ID --retry.
  • The offline messaging example walks through a reply, a scheduled delivery, a direct send and a failed send's retry; fault tests cover redelivery, ACK ordering, cancellation, configuration changes, independent recipients, including stream subscribers a scoped watch does not run, queue recovery and provider-neutral transport boundaries.

Changed

  • bf init names channels/ beside sensors/, routines/ and prompts/ in the brain's AGENTS.md and lists bf channel and delivery bf queue ID --retry among the commands that need explicit authority, as bf-use, bf-action and Concepts now do; Configuration and troubleshooting count channel and queue among the commands that act on one brain.
  • Unscoped and identity searches, memories pages and the home page read each source's record count, latest event and latest change from one index instead of every record's row. On the benchmark's 20,000 records, a selective search takes about 16 ms instead of 42 ms and the home page 14 ms instead of 48 ms (median in-process timings on one Linux machine).
  • A mistyped source suggests close ones, as a mistyped page or note does: bf read memories/gmial and bf search … --scope memories/gmial end with did you mean memories/gmail?.
  • Development tools: uv 0.13.1, with the build requirement widened to uv_build>=0.12.10,<0.14; dprint 0.63.0 with the JSON 0.25.2, Markdown 0.26.0 and TOML 0.9.0 plugins; lefthook 2.2.2; trivy 0.76.0; and no jq, which no task, hook or test runs. mise.lock uses lockfile revision 3, which needs mise 2026.9.16 or later, and the workflows pin mise 2026.10.12, jdx/mise-action v5.2.0, actions/upload-artifact v7.0.2 and actions/download-artifact v8.0.2. .python-version names Python 3.14.8, the mise pin, which stays below 3.15 while PyYAML ships no cp315 wheels.
  • The locked environment moves to mcp 2.3.0, pydantic 2.14.1 with pydantic-core 2.50.1, typer 0.27.3, starlette 1.8.0, truststore 0.10.5, opentelemetry-api 1.45.1, rpds-py 2026.9.1 and pycparser 3.11, and for development to zensical 0.0.71, ruff 0.17.1, ty 0.0.89, pymdown-extensions 12.3, markdown 3.11.1, markupsafe 3.0.4, pluggy 1.7.0, iniconfig 2.3.1 and tomli 2.5.0; the package's dependency ranges are unchanged.

Fixed

  • Replies, export lines, watch --json snapshots, diagnostics and MCP errors escape the line and paragraph separators U+2028 and U+2029, and so do bf-use's evidence helper, the example hooks and the weekly review: a file name in a shared brain can no longer split a JSON or context line, or forge a second bf: line, for readers that break lines there, such as Python's str.splitlines().
  • bf watch finds notify-send, gdbus and osascript for desktop alerts only through absolute PATH entries, like the programs it runs: with . on PATH, a watcher started in the brain root used to run a notify-send the brain supplied on every alert.
  • The example Calendar, Drive and Gmail sensors start gws from /, so a .env file in the brain or a parent folder, which gws loads, no longer switches the account they collect from. The Git and GitHub history sensors start git and gh there too, and git-history.py resolves a relative ROOT against the brain folder, where BF runs it.
  • Programs no longer inherit R_PROFILE, R_ENVIRON, R_ENVIRON_USER or the CORECLR_* profiler variables, which make R or .NET load code before the program starts. Limits names them, instead of claiming every .NET startup option is removed, and shows how a program sets one on purpose in its own command, such as [env, R_ENVIRON_USER=/path/to/Renviron, Rscript, sensors/example.R].
  • A search or read that builds a missing cache, as after an upgrade, a structural bf.yaml edit or a damaged cache, compares the new cache with the brain once more before answering: a collection committing during that build used to leave the reply holding records it had removed and missing those it added, without marking it stale.
  • A search cache build that publishes while the home page is being read no longer fails: the page's attention opened the cache a second time, behind the publish, so the read waited 30 seconds and the build failed with readers kept the search cache open; retry bf build. A first build no longer creates an empty .bf/index.sqlite with default permissions just before publishing, which a build interrupted then left behind.
  • A refreshed cache matches a full build: restoring the permissions of an unreadable folder inside memories/SOURCE is seen at the next search or read instead of only after bf build, and spelling suggestions no longer depend on the order files were indexed in, where a refresh could suggest policy and a full build of the same files policies.
  • A brain whose .bf directory is read-only fails with the search cache needs write access to the brain's .bf directory, also from bf status, bf export and bf build, instead of naming search and read.
  • A link, and a file standing where projects/, concepts/, actions/ or memories/ belongs, name their cause and repair alike in search, reads, bf status and bf validate: a link says to replace it with the file or folder itself, where a linked memories/ or synced concepts/ folder was told to become a regular file, and such a file says expected a folder; replace the file with a folder, where search reported a misnamed record and bf validate a special file. A link, special file or unreadable folder named *.json reports its own problem instead of expected memories/<source>/<sha256-id>.json, even in projects/.
  • An action read names each skipped file in its folder once, with its real cause, such as symlinks are not followed, even past the cache's first 200 problems: it repeated each as symlinks and special files are not listed, the wrong cause for an unreadable folder. Replies count a single skipped file beyond the first 200 as 1 more file was skipped.
  • Reading a linked or special folder below an authored root, or a folder below one, such as projects/shared/sub, names that entry (projects/shared: expected a directory; symlinks and special files are not followed) instead of answering reference not found; a selected brain that holds a real folder there still answers.
  • Search coverage keeps a source that fell silent, marked "silent": true, beside failed, overdue and never-collected ones: an empty search used to describe a scheduled source that stopped bringing evidence as fresh. The read reply schema, and with it MCP's outputSchema, describes the "silent": true that home attention entries carry, which clients validating replies rejected.
  • bf status and the memories pages state a source's changed with your local offset, to the second, like latest and last_collected: as a UTC instant with microseconds, it misordered them when compared as text.
  • Source period, undated and record-file pages give the source's coverage, as memories/SOURCE does, so an empty day no longer reads as absence without saying whether the source was collected. Tag scopes and tag searches, which hold only notes, no longer list collected sources.
  • Later text pages of a large exact read carry only the read's own problems and stale, as documented, even when the graph context alone made the reply page: a note just under 32 KiB used to repeat its context's problems on every page.
  • Tag errors name the mistake: an invalid label reads invalid tag label; read the tags page and use one of its refs, in MCP too, which said use a ref returned by the read tool tags; bf read bf://brain/tags/a%2Fb states the label rule (invalid tag address; tag must be 1-128 characters …) instead of a section or relation it does not have; and bf read tags/C# points to the bf:// ref that bf read tags returns for a label holding #, instead of advising to drop the fragment, which reads the label C.
  • A claim to a note section, such as bf://brain/projects/new-website.md#decision, names that note as target_ref with its target_title, as the linking guide shows, while its target keeps the section; a target that is both a record's ref and another note's alias names no item instead of the note. A read or search no longer drops an owner's aliases and reports a false conflict when another process, such as bf watch, re-indexes the owner's note at that moment.
  • Identity problems read correctly: one shared alias reports aliases also claimed elsewhere were not expanded: 1 instead of 1 aliases are …, and an identity that several notes or records hold says to read an owner's bf://NAME/... address, since the owners may be records, such as one record two brains collect.
  • Previews end in … where they cut, at a word when one is near: excerpts in listings, relation pages and identity searches stopped mid-word at 320 characters without a mark, such as We choose selected captures t, and read as complete, and backlink excerpts cut mid-word at 160. The tasks page and each claim's target_title cut titles to 200 characters, like other listings: a note titled with a whole message fit only 7 tasks on a page, and claims naming such targets could push a small note's exact read past 32 KiB.
  • A note holding one long line dense in punctuation, such as pasted minified JSON, parses in linear time: a 4 MiB note took minutes, stalling search refreshes, bf watch and bf validate.
  • Body links to records of sources named file, data, javascript or vbscript, such as [Notes](file:notes.txt), count as links: markdown-it's HTML filter dropped them silently, with no claim, backlink or validation, while the same ref in frontmatter links counted.
  • Footnote syntax shown in an indented code block, or an underline right below a definition, even in a block quote, no longer creates sections: a code line could become a searchable heading whose links failed bf validate, and --- or === turned the definition into a heading. As on GitHub, --- there is a rule that ends the footnote, and a definition indented four columns or more, as in a nested list item, continues on lines indented four more.
  • Replies state an instant from before its timezone adopted standard time, such as an 1850 archive record, in UTC (+00:00): its local mean time offset, such as New York's -04:56:02, has seconds, which RFC 3339 date-times and the reply schemas reject.
  • When bf build, bf collect or bf update cannot apply an interrupted write's journal, the error names memories/.pending, and an unexpected file there by name: a manifest that is not JSON read invalid JSON document, as a sensor's bad output does, and a missing backup an inaccessible file. Troubleshooting and bf-maintain say how to recover when the journal itself is refused.
  • bf register takes over a name whose registered folder no longer holds a bf.yaml, as after a move or over an unmounted share, instead of advising you to rename one of the brains, and search, read and bf status report such a folder as bf.yaml is missing, which troubleshooting and bf-maintain cover. Registering again where nothing moved keeps the entry as written, ~ paths included, even with a home folder reached through a link, such as /home to /var/home; the registry keeps every paragraph of its leading comments, and a new one starts with the configuration page's URL, as documented.
  • A collection or build that readers block, such as during a long bf validate, says another process is reading or changing this brain's records instead of naming a writer that does not exist, and a bf register waiting for another registration says another bf register is updating the registry.
  • bf collect, bf run and bf update no longer stop with a traceback when no home directory resolves, as for a container user without HOME or a passwd entry: programs that do not use {{home}} run, and one that does fails with cannot find the home directory for {{home}}; set HOME, which a sensor's run history records with the failure backoff, as for routines. bf schedule --backend launchd and a bare ~ path, such as bf schedule's default ~/.config/bf-schedules, also say to set HOME.
  • keep never removes the output of the run that just wrote it, even when another clone in a time zone ahead dated an action later or a clock that ran ahead dated older assistant runs later: the reply, run history and bf status then named an action or assistant record that no longer existed. A routine whose keep cannot read or remove an older action, such as one in a read-only folder, still succeeds and logs/NAME.log names the folder it kept, as for a link or unreadable folder in actions/, which pauses removal, instead of failing and retrying; an assistant's keep prunes past a run record it cannot read, which stopped all pruning.
  • A routine's broken-link error quotes at most 200 characters of the link, as Limits now lists: a long target taken from evidence no longer fills run history and every bf status reply.
  • After a clock correction, an assistant schedule whose last success lies in the future runs at once, as bf status and dry runs report, instead of skipping for up to one refresh.
  • bf schedule names the existing file that differs from what it would generate, instead of schedule file already exists with different content, so you know which one to review.
  • bf watch stops its collection updates within one 60-second bound in total and says so once, instead of 60 seconds and a notice for each. A second Ctrl-C, or the hangup of a closing terminal, no longer skips the wait and kill: watch exits 130 once every update has stopped or been killed.
  • The watch dashboard keeps interval too slow, press ? whole in its header at the documented 80 by 24 minimum, where the summary cut it off below about 90 columns, and its guide keeps its control and freshness lines instead of repeating the footer's keys. The selected row stays readable on light terminal themes, and configured periods read exactly: a 90-second interval is Check: 90s and a 5,400-second refresh Refresh: 90m, not 1m and 1h.
  • The watch dashboard reads 1 program, 1 needs attention and Search cache skipped 1 file for one, shows Checks paused while paused instead of a countdown stuck at 0s, and says Nothing needs attention when Tab's attention filter hides every healthy program, instead of asking you to add one to bf.yaml.
  • Watch details name the command that runs the selected program now, bf collect NAME for a sensor and bf run NAME for a routine or assistant, and none for a disabled program or in an observer: they suggested bf collect for routines as well as sensors, and a command for disabled programs and in an observer. A second bf watch that falls back to observing no longer warns that its own unused interval runs programs late, and a busy bf watch --json reports only that another watcher owns the brain.
  • bf update --kind checks its value before selecting a brain and quotes it like other choices: --kind "" reads '' is not one of 'sensor', 'routine', 'assistant' (exit 2) instead of a blank name or a brain selection failure. bf run refuses a program name that is not UTF-8 as invalid input (exit 2), while the arguments after it, such as a hook's Git arguments, reach the routine byte for byte. Of two bf init runs on one empty directory, the second fails instead of replacing the first one's files.
  • Help matches behavior: bf --help says register selects a brain by name outside its folder and validate also checks program files; status --check names an unavailable cache and sources that fell silent; collect --allow-removal states the snapshot guard's 10-record floor; every --notify mode except off alerts on recovery; bf skills never overwrites edits without --force; and collect --until and register PATH state their default once.
  • bf eval ignores an editor's .#NAME lock beside a suite, as validation and search do beside notes, so a suite open with unsaved edits in Emacs no longer fails every run, and another link in evals/ fails with validation's reason. A folder without suites is named in the advice, as in projects holds no .yaml or .yml suites; add one, such as projects/retrieval.yaml, …, and a --path naming neither a folder of suites nor a .yaml or .yml suite, even below a note, says so instead of suggesting evals/retrieval.yaml or replacing the note with a folder.
  • A whole-note expect or forbid ref matches the sections of a note whose file name holds #, such as projects/C# guide.md, and a read case can expect the bf:// address of a record whose id holds #, such as bf://NAME/issues:item%23comment: such an expectation failed with a null rank, and a forbidden note passed.
  • bf status outside a brain reports a registered brain whose bf.yaml cannot load beside one that is unavailable, exiting 0 (1 with --check), instead of failing with the bare file error. A brain's uv.lock nested past Python's recursion limit or holding an integer over 4,300 digits pins nothing, like any lock that does not parse, instead of failing every selected brain with a traceback.
  • bf-use's helpers: evidence.py compare compares a record's time as an instant, so a read made in another time zone, such as a teammate's, no longer reports an unchanged record as changed; capture and compare take up to 19 MiB of standard input per reply or capture, instead of 9 MiB in all, and name the bound beyond it; and guarded-write.py refuses content above 4 MiB, the largest note bf reads, instead of writing up to 16 MiB, which left a note that every search reported as a problem.
  • bf-action's new-action.py accepts topics that start with a digit, such as 3d-printing, as bf validate does, says when --brain is not a brain directory instead of hinting at permissions, and prints compact JSON, like bf and the other helpers.
  • The Claude Code command of the assistant guide and bf-maintain's assistant example keeps the harness to the Read, Grep, Glob and Bash tools, allows only bf read and bf search commands and, with --permission-mode dontAsk, refuses any other call, even when your settings accept edits: the skill's --tools "Read,Grep,Glob" left out Bash, so the harness could not run the bf read and bf search its prompt asks for and answered without reading the brain, and the guide's command left every other built-in tool, such as Edit, to your settings' permission mode while its prompt reads untrusted evidence. The skill, the assistant guide and troubleshooting say a --tools list must keep Bash for such rules, Add a sensor restricts its meeting-notes agent the same way, and the skill's sensor example declares the kind field it maps, which a brain created with bf init lacks.
  • bf-use's concept template names every relation bf init declares, author, owner, depends-on and related-to: declaring owner or depends-on again, as it advised, failed bf validate on a duplicate key. Its retrieval guide reads a section the outline lists, or redirects evidence.py read to an owner-only file or a pipe: run bare, that printed a large note's whole text past the 32 KiB page budget.
  • The example weekly review no longer lists itself as needing attention on its first run, after its own failure or after a long pause, a line that alone wrote an action on an otherwise empty brain: BF records a run only after it exits, and the review recognizes its own by file name, so keep it named weekly-review, and by its brain's name, which it asks bf status for when the home page lists brains declared under brains:, so their weekly-review stays listed. It counts as recent only actions begun in the last seven days, up to today, names the bf read page holding the rest when a preview is full, says why it lists a silent source, writes 1 open task and 1 note in the singular and names bf read projects among its sources.
  • The example session hook labels a source that fell silent silent instead of fresh, while a failed or overdue one keeps that state, since a stopped sensor falls silent too, and lists a note linking the project with several relations once among the three newest linking notes. The example prompt hook reads prompts in NFKC form, as bf search does: pasted text with decomposed accents searched fragments such as cide instead of décidé.
  • The example Gmail headers sensor keeps at most 1,000 from_refs, like to_refs and cc_refs: a message with more senders failed every collection that mapped them, and its window never advanced.
  • Release tooling: scripts/verify-release.sh accepts only build attestations that CD signed for the release tag and its commit, where one from any workflow or ref of the repository passed, and ignores a relative XDG_CACHE_HOME, as bf does; release notes stop at the next level-2 heading, so an ## Unreleased placed below a release no longer publishes its draft entries; scripts/push-and-tag.sh exits 1 with pushing main failed; nothing tagged when pushing main fails; and CD fails at the hash extraction, with jq's error, when PyPI's release metadata cannot be read, instead of reporting a hash mismatch.
  • Contributor checks: tests validate each search or read reply against the schema of its own kind, ignore the host's TERMINAL_WIDTH, PY_COLORS, TYPER_USE_RICH and _TYPER_FORCE_DISABLE_TERMINAL, give cold starts and the MCP handshake the documented 120-second deadline and cover more release-script failures; the contract check fails when a configuration oneOf alternative accepts more than its release; check:links checks anchors in repository Markdown; a workflow step that pipes commands must set shell: bash; the README test checks its images and badge against the checkout; and the scale benchmark times a misspelled word, rejects more records than one collection commits, writes its corpus under the writer lock and ignores a relative XDG_CACHE_HOME.

Documentation

  • The bf-use and bf-maintain skills, Search and read, the retrieval reference, troubleshooting and the hooks guide cover silent sources: such a source may lack evidence while looking fresh, joins attention and the home page and fails bf status --check. The retrieval reference explains the changed and silent coverage fields of memories pages and that only the memories overview gives each source's page.
  • Editor and reply schemas describe more: a registry entry, a field's description and type, an evaluation suite's cases, the changed and silent coverage fields, and that routine names must differ from assistants as well as sensors.
  • The hooks guide, agent workflows, privacy page and team guide say to copy host hooks into a folder only you write and never register one from a brain others can push to: the host runs a registered hook at every session or prompt, so a pulled change would run unreviewed. Before running a shared brain or a clone, also review prompts/ and channels/, which the brain's assistants run, any .env file, which a provider CLI such as gws loads from the brain's folder and which can switch its account, and the agent configuration an assistant's harness loads there, such as Claude Code's .claude/, whose hooks --print runs without a trust dialog, and .mcp.json; Assistants shows how --setting-sources user keeps the brain's settings files out of a run. The team guide adds that a clone's bf status --check fails on scheduled sources it did not collect, which show never under attention.
  • Assistants explains that a native schedule runs assistants in the same bf update job, so under systemd or launchd a long agent run delays the next collection, and how to give assistants a job of their own, as bf-maintain now advises. Assistants and Privacy warn that a {{prompt}} argument shows the expanded prompt, a piped message included, to local process listings while the harness runs, and Watch and schedule says generated schedules also copy XDG_CONFIG_HOME and XDG_STATE_HOME when set.
  • Install and update covers every release since your previous version: apply each release's upgrade steps oldest first, copy the examples it names from the tag matching bf --version, and repeat bf skills for each folder you installed the skills into, such as ~/.claude/skills. The 18.5.0 notes now say to merge the AGENTS.md line naming prompts/, and the 14.0.0 notes give the upgrade steps from 13.
  • The link reference corrects its recovery advice: a BF address decodes its percent-encoding, so %E2%80%8B still fails, and only a record whose aliases hold an invisible character or exceed 1,000 stops loading. Only an OKF sources resource written as a link, without spaces, cites and is checked, so write ./brief.txt for a sibling file; the 8,192-character bound covers BF links and frontmatter links, which the note field table now lists; listings show single-value relations too; and a note's entity may be any explicit identity, usually a bf:// name.
  • Limits states that every run ends by killing the program's process group, so background helpers end with it unless they start their own session, that standard input is /dev/null and only a helper holding standard output open fails a finished run, that a folder nested deeper than 64 levels fails the scan, that only the brain's root inputs/ and originals/ go unscanned, and the *_truncated flags of validation's unresolved targets and reprojection problems. The retrieval reference adds that a note saved in place twice at the same size within one timestamp tick, with a search in between, stays unseen until it changes again or bf build.
  • The format and retrieval references match behavior: True, TRUE, False and FALSE are booleans and 017 is decimal; a stored revision whose updated is later than its observed, as from a clock in the future, never freezes a record; only the Markdown files of an action's inputs/ and outputs/ are searchable; a record file edited by hand waits for bf build; and today and yesterday name their previous and next days, while projects list newest updated first.
  • Commands, Configuration and the MCP contract match behavior: a bf status brain lists routines and assistants only when it declares any, and the status example shows a source's changed and every last_run counter; duplicate keys and YAML syntax errors name the line and column; one brain folder cannot serve several accounts, since BF writes owner-only files, so each person uses a Git clone; and an MCP invalid input: error can name offset too.
  • Check your brain lists ambiguous and folder-page identities, unreadable files, a file where a scanned folder belongs, the action-folder rule, the 120,000-byte prompt bound, unresolved_truncated, a case's limit (1–50, default 10) and when mrr appears, and says a window sensor needs a backfill to clear record-link and case-variant warnings. Troubleshooting covers bf.yaml is missing, … not found and unknown source suggestions and a file in a folder's place; its [cat] check dry-runs a temporary assistant without text, instead of swapping a live assistant's command, which fails its queued work, and pipes a message with --stdin, its Updates do not run section links the scheduler's host lifecycle, and recovery no longer promises to take about a second.
  • Agent workflows describe Context as a standard section of every action and say a forced bf skills update still lists edited files; bf-use's guarded-write guide says Written, but its directory could not be synced exits 1 although the file changed; and bf-action lists outdated evidence, not stale evidence, among an action's unknowns, since a stale reply calls for a retry.
  • The example guides match their programs: the Gmail headers guide says to authorize gws with gmail.readonly and without gmail.metadata, since Gmail rejects the sensor's search for a token holding that scope, and names its typed-relation attributes and label ids; the sensors guide gives each sensor's time limit per provider call beside the whole-run timeout, lists the supported document formats by suffix and says a symbolic link or special file fails the snapshot whatever its suffix; the hooks guide asks for the bf release whose tag the hooks came from; and the GitHub history guide names its lowercase repo:github.com/OWNER/REPO link.
  • The walkthroughs match their output, and tests check more of what they state: the weekly review walkthrough shows the action's seven open tasks with their section refs and its records by source; the offline watch demo prints its directory before the dashboard covers the terminal, runs its other commands in a second terminal and a checkout's bf as uv run --project CHECKOUT bf, and counts Items as the dashboard does; the assistant example no longer claims bf validate checks the harness is on PATH; Getting started lists the .gitignore that bf init writes; Search and read continues a listing on the Getting started brain; and the four-tool, team, retrieval and example-brain walkthroughs state their trimmed replies, action suffixes and counts as their brains hold them.
  • The documentation site builds with Zensical 0.0.71, whose search dialog's close button screen readers announce as "Close search" instead of "Search", and its third-party notices link the license texts of Preact, Preact Signals, clsx, usehooks-ts and lodash.debounce, which Zensical's search client bundles but its license file omits; check:notices fails when one of those copies or a Zensical version row goes missing or changes. In light mode, links and accents use the logo's blue and the Fmind palette's purple.
  • The README and the loop diagram include assistants and channels in what bf.yaml declares, the README's figures for the author's brain follow the count of 2026-10-16 (more than 80 sensors and about 50,000 records), and the PyPI page links the changelog.
  • Contributing starts setup with mise trust and mise install --locked, since tasks never install tools, and notes that check, and so the pre-commit hook, needs network access for its vulnerability scans; the release checklist explains that bash ignores set -e in any command whose status if, && or || tests, as an agent's shell wrapper does; and the pull request template asks a breaking change for its manual upgrade steps under Unreleased.

v18.5.0 - 2026-10-14

A feature and review release. The brain format (version: 7) is unchanged; bf.yaml gains the optional silent_after and keep settings, which an older bf rejects; the reply schemas only gain optional fields, and the search cache rebuilds once. Upgrade with uv tool upgrade brain-framework (or update a brain's pin), run bf skills DIR to update all four skills, copy the updated example weekly-review routine and gmail-headers sensor into your brain, preserving local adaptations, and merge the AGENTS.md line that now names prompts/.

Added

  • A sensor's silent_after (seconds) reports a scheduled source that keeps succeeding without a new or changed record: bf status marks it "silent": true, and attention, the home page and bf status --check list it. Freshness proves a successful run, not new evidence: a filter or account drift used to look healthy. bf status and the memories page give each source's changed, the newest time this brain gained a new or changed record of it.
  • keep bounds generated output. An action routine's keep: N removes its actions older than the newest N while they still hold only the ACTION.md this machine wrote: an edited report, a folder holding another file and an action written before keep stay. A prompt's keep: N removes its oldest run records after each run, never another file of its folder, and never fails a recorded run. Replies count them as removed.
  • bf status names each brain's runtime: the installed bf version and, read as data from the brain's uv.lock, the pinned one. Agents compare their major versions instead of parsing the lock.
  • bf validate reports an enabled prompt file above the 120,000-byte limit, which every run would reject.
  • A runnable offline prompt example, examples/prompts/, uses cat as a stand-in harness with refresh: 0: a piped message becomes a record that search finds. The prompt guide starts with it, and troubleshooting covers failed prompts: JSON output with text, the 120,000-byte limit on standard input too, skipped scheduled runs, harness sign-in and tool permissions.
  • bf-maintain covers prompts: declare one with an explicit refresh (an enabled prompt runs every 30 minutes by default, unlike sensors and routines), a timeout above the harness's own limit and a text pointer; test it without a model through command: [cat], then run it once with bf run PROMPT --stdin. It treats memories/NAME/ records as model output and grants the harness only the tools it needs.
  • Reads return backlinks_omitted, the number of relation groups beyond the 64 they preview, counted across the selected brains: more than 64 relations used to drop the rest silently.

Changed

  • A program another process is running, such as a manual bf collect during bf watch, is skipped in bf update instead of failed: watch no longer alerts a failure that run history never records.
  • bf run suggests close prompt names too: unknown routine or prompt asistant; … did you mean assistant?.
  • A scheduled prompt that is not due when it takes its lock, because another process ran it or it waits to retry a failure, says so instead of claiming another run just succeeded.
  • The four skills are 30% shorter (17,009 → 11,919 words) without dropping a procedure or safety rule: each shared rule lives once, references link the documentation for exact contracts, and long steps are split. They compare runtime.pinned with runtime.installed in bf status instead of reading a brain's uv.lock.
  • bf-action defines when an action is done: its Outcome is filled and its TODO has no open box. Discovery resumes only open actions, closing resolves every open task, and later work starts a new action linking the done one.
  • bf init names prompts/ beside sensors/ and routines/ in the brain's AGENTS.md; merge that line as Refresh brain instructions shows.
  • The documentation gives each rule one owner: timing and failure backoff in Watch and schedule, program names in the file formats, ranges in Limits, dry runs and status fields in Commands, scopes and incomplete answers in Retrieval. The README's Try it installs BF and links Getting started; the guides run copied example scripts as executables.
  • The example weekly review lists scheduled programs that failed or are late, says when an empty week or coming week may come from a sensor needing attention, and writes a review when attention is its only content.
  • Searches, reads and each MCP call no longer check every record file: the cache keeps each memories/SOURCE folder's fingerprint and lists only folders that changed, so on a brain of about 50,000 files a warm search checks its cache in about 16 ms instead of 170 ms (in-process timings on one Linux machine). Collection renames records into place, which always changes their folder; a record file edited in place, which collection never does, waits for bf build. Authored folders, bf build and bf validate still list every file.
  • The home page lists projects needing review first in the space changed used to take: changed names only other notes, in brief (ref, kind, title, date or time, modified, review), and the 32 KiB budget is measured on the reply as printed. A brain whose changed list repeated its projects used to show only one of the projects due for review.
  • Deep listing pages stop at their window: with one selected brain the offset is skipped in SQL, so a source page at offset 8,000 answers in about 65 ms instead of 300 ms on the same machine, and later pages of a large exact read no longer rebuild its backlinks.

Fixed

  • bf watch stops a collection whose start a cancellation interrupted, and signals its collection and prompt workers together, once each, so they stop in parallel.
  • bf update dates its due list once it holds the cycle lock: waiting for another update no longer leaves newly due programs for the next cycle.
  • A prompt answering a message and a scheduled run of the same prompt no longer overwrite each other's log entries.
  • A prompt file above the limit reports file exceeds 120000 bytes instead of 120001.
  • bf build --reproject --dry-run counts changes without holding every reprojected record, and keeps only the failures its reply shows.
  • Programs no longer inherit DOTNET_STARTUP_HOOKS, R_PROFILE_USER, LUA_PATH or LUA_CPATH, which load code before the program starts.
  • The contract check accepts a configuration value that stops listing its allowed values, which widens what files accept, as a minor change.
  • Tests give cold interpreter starts the documented 120-second deadline and cap parallel workers at eight, so a loaded machine no longer fails the gate.
  • guarded-write.py serializes the check, write and rename of one file among concurrent helpers with an advisory lock below XDG_STATE_HOME (or ~/.local/state), so of two sessions that read the same digest only one succeeds; before, both could report success while one edit was lost. An editor that takes no lock can still save between the helper's last check and its rename.
  • evidence.py read keeps the stale and problems of every page of a large exact read, so a capture refuses, and a comparison reports unknown, when a later page was incomplete.
  • The example Gmail headers sensor no longer misses messages received in the first or last fraction of a second of its window: it widens Gmail's whole-second search outward and filters exactly.
  • The prompt guide's Claude Code command allows the read-only bf read and bf search calls its prompt asks for, and the release checklist reads the workflows' mise pin despite its inline comment.
  • Documentation matches behavior: --prompt and update --kind repeat, prompts get name suggestions, a prompt dry run appends to its own log, program names are unique across sensors, routines and prompts and never bf, bf init creates no prompts/ folder, and an exported BF_BRAIN takes precedence over the working directory.
  • A read of a note ref ending in a bare # is invalid input (exit 2), like a scope: it read the whole note under a ref no backlink or reminder matched, so the note listed itself as a backlink and lost its tasks and review signals. An offset on the home page or the memories overview exits 2 too.
  • bf validate resolves an alias that a later note declares before checking links, instead of reporting a missing record that exact reads resolve.
  • Spelling suggestions fold Latin accents as promised (equipe, not équipe), and a paged read without sections inside it omits outline instead of returning outline: [].

v18.4.0 - 2026-10-13

A feature release. The brain format (version: 7) and the search cache format are unchanged; bf.yaml gains an optional prompts: section, which an older bf rejects, and the reply schemas only gain optional fields. Upgrade with uv tool upgrade brain-framework (or update a brain's pin), run bf skills DIR to update bf-maintain, which now covers prompts, and the retrieval guide, and copy the updated example hooks and weekly-review routine into your brain, preserving local adaptations.

Added

  • Prompts run your agent harness over the brain, every 30 minutes by default or now with bf run PROMPT --stdin. Declare one under prompts: with a Markdown prompt file in prompts/ and a direct-argv command: BF expands {{brain}}, {{home}}, {{start}}, {{end}} and {{input}} in the file once, then passes it as the command's {{prompt}} argument or, without one, on standard input. BF never runs a model itself. Each successful run becomes a record in memories/NAME/ with the reply as its text (text selects it in JSON output, such as /response), its input, window, duration and the harness's other output, such as token usage, so later runs, bf search and your agents read it back; those records rank like a low-priority source, so a summary never outranks its evidence. See Run prompts.
  • bf update runs due prompts after sensors and routines, outside the update lock; bf watch runs them in a second process beside collection, so a long agent run never delays a sensor, and marks them @ with their run counts. A direct run never waits for a scheduled one and leaves the schedule's history unchanged, failures included; a scheduled run checks again under its lock that no other process just ran it.
  • bf update --kind sensor|routine|prompt, repeatable, keeps only those kinds of program; update, watch and schedule accept --prompt NAME selectors.
  • The example session hook names failing or overdue prompts in its attention line, beside sensors and routines.
  • bf status reports prompts with their state, freshness, runs, last run, latest record and failures; attention and the home page list failing or overdue prompts, and bf validate checks that each enabled prompt's file exists.

Changed

  • bf run takes a routine or a prompt: its argument is named PROGRAM.
  • A routine's {{prompt}} placeholder is invalid, as before; only a prompt's command takes it.
  • CI and security workflows pin jdx/mise-action v5.1.1. The explicit mise 2026.10.0 pin keeps tool-manager upgrades deliberate; the action's 24-hour release-age default applies only without that pin.

Fixed

  • Cancellation during sensor or routine startup now kills the newly created process group. Markdown complexity tests compare CPU-time growth, and cancellation tests wait for observed process termination, avoiding fixed-speed assumptions on slow CI runners.
  • Period pages and search scopes place records without time by attributes.observed, including source counts and ordering across brains. Replies keep observation separate from event time; records with neither timestamp remain outside periods, and undated still selects every record without event time.
  • The weekly-review example lists projects as plain bullets, with overdue reviews first and earliest deadlines first, and separates the open-task preview from its surrounding paragraphs. Generated reviews no longer create duplicate checkbox tasks; complete work in the owning notes.
  • The session and prompt hooks keep nonempty stale cache results while collection runs, showing “Index refreshing”. They tolerate only the exact stale-cache identity problem; empty generations, malformed metadata and other problems stay silent.

v18.3.0 - 2026-10-07

A review and feature release. The brain format (version: 7) and bf.yaml are unchanged; the reply schemas only gain optional fields and the search cache rebuilds once. Upgrade with uv tool upgrade brain-framework (or update a brain's pin). Run bf skills DIR to update bf-use, and copy the updated example hooks, weekly-review routine and any new sensor into your brain.

Added

  • bf search suggests close words the brain holds for each misspelled word: "suggestions":{"retension":["retention"]} beside unmatched.
  • A claim names the note or record that owns its target, when exactly one item of its brain does, as target_ref and target_title: follow it without another search.
  • bf collect --dry-run reports the added, updated, unchanged and removed counts a collection would commit, and "removal_refused": true when the snapshot guard would refuse it, before you pass --allow-removal.
  • bf watch warns, like bf schedule, when its interval is at least the shortest selected refresh, which runs that program late and makes bf status report it overdue.
  • A mistyped --brain name suggests close registered, enclosing or referenced brain names: did you mean work?.
  • The example gmail-headers.py sensor collects Gmail senders, recipients, subjects, labels and dates through gws, never message bodies, with person:email/ links, a gmail-thread: link per conversation and an RFC 2392 mid: alias.

Changed

  • Writers also lock the brain folder itself: two processes with different XDG_STATE_HOME values, or two accounts sharing a brain, used to commit at once, and one's recovery could roll back the other's committed records. A filesystem that cannot lock a folder, such as NFS, keeps the state lock alone.
  • The weekly-review routine names the projects to review by ref instead of linking them: its links flagged every listed project with newer evidence, so each later review counted the earlier ones.
  • bf init writes its starter files from the package's starter/ folder; the generated AGENTS.md now holds one line per paragraph, so the refresh comparison shows every wrapped line once.
  • A search scoped to an authored folder no longer counts collected sources, and a cache is checked against the layout bf builds in one comparison. Tests run in parallel.

Fixed

  • bf eval ranks expected refs among the first 50 results even when they span several reply pages: a search reply stops at its 32 KiB budget, so a ref at rank 46 among long results read as missing and MRR dropped.
  • An action's projects include projects its ACTION.md links by entity, alias or BF address, as each project's backlinks already listed the action; only path links counted.
  • An identity whose BF address percent-encodes a format character, such as bf://brain/people/bob%E2%80%8B, is invalid like its raw form: it looked like another subject's address.
  • A footnote-looking line inside a code fence no longer hides the sections after it: its indented lines closed the fence early.
  • Period pages and scopes accept a trailing slash, such as bf read 2026-09/ or --scope 7d/, like folder and tag pages.
  • A file that failed to read for a reason other than permissions, such as an I/O error on a network mount, is read again on the next refresh instead of staying skipped until it changed; the search cache rebuilds once.
  • A cache that another program locks, such as an open sqlite3 shell, names the lock instead of advising to check free space.
  • bf status lists programs named date, time, fields or attributes like any other.
  • bf init, bf skills and other commands reject an argument that is not UTF-8 as invalid input before writing; init and skills used to create every file, then fail.
  • Recovery clears an interrupted record transaction that left one temporary file beside a full journal; every later collection used to fail.
  • bf watch waits for and stops its update when the terminal closed while it was stopping, instead of leaving it running.
  • The bf-use guarded-write.py helper refuses a passage that occurs twice in overlapping positions, as it refuses other repeated passages.
  • The example hooks and the weekly-review routine escape control and invisible format characters, as bf replies do: decoding the JSON restored them, so a shared note's title could reach the agent's context or a review action as hidden text. Copy the updated examples into your brain.

v18.2.0 - 2026-10-06

A skill release. The brain format (version: 7), bf.yaml, the reply schemas and the search cache are unchanged. Upgrade with uv tool upgrade brain-framework (or update a brain's pin), then run bf skills DIR to install bf-action and update bf-use.

Added

  • A fourth packaged skill, bf-action, starts, resumes, hands off and closes a tracked work session. Invoking it with a topic, such as /bf-action website-review in Claude Code, is the request to start that action: the agent resumes an open action on that topic or, when none exists, finds the owning project, creates actions/YYYY-MM-DD_website-review/ACTION.md and fills its Context, TODO and Resume, with no need for wording such as "start an action". The action guide, the working-context and handoff guides, new-action.py, check-handoff.py and the action template move from bf-use to bf-action; bf-use keeps retrieval and writing knowledge back.

Upgrade: run bf skills DIR to install bf-action and update bf-use, which removes the moved files. If you edited any bf-use file, the folder reads modified and stays unchanged: back up your edits, then rerun with --force, which keeps edited files the new version no longer ships. A Claude Code SessionStart hook that runs bf-use/scripts/check-handoff.py must point at bf-action/scripts/check-handoff.py. bf init now names both skills in the brain's AGENTS.md; merge that line as Refresh brain instructions shows.

v18.1.5 - 2026-10-06

A search fix. The brain format (version: 7), bf.yaml, the reply schemas, the packaged skills and the search cache are unchanged. Upgrade with uv tool upgrade brain-framework (or update a brain's pin); a copied prompt-context.py hook can take the example's new word list.

Fixed

  • Search drops other, others and another, and the French autre, autres, ma and ta, like other function words. In a mostly English brain, Pourquoi Kotlin plutôt qu’un autre langage ? ranked first a short French note that only shared autres with the question, a rare word that outweighed the subject; the note about Kotlin now ranks first. A query made only of such words still searches them literally. The example prompt-context.py hook mirrors the list.

v18.1.4 - 2026-10-04

A review release of fixes. The brain format (version: 7), bf.yaml, the reply schemas and the packaged skills are unchanged; the search cache rebuilds once. Upgrade with uv tool upgrade brain-framework (or update a brain's pin); no other step is needed.

Fixed

  • Search no longer lands on a section without the matching text: in a copied document with several H1s, text under a later H1 ranked as the H2 above it, whose exact read stops at that H1. An H1 after the first section is now a section of its own. The search cache rebuilds once.
  • A record's fields other than text are bounded by their encoded size, as the reply limit counts bytes: a record of long non-ASCII links or aliases passed the 2 MiB check, then every exact read of it failed with response exceeds its byte limit. Collection now rejects it with fields other than text exceed 2 MiB.
  • A registered brain whose folder exists but cannot be reached, such as a locked folder, is reported as registered brain directory exists but cannot be reached; restore access to it by search, read and bf status instead of as absent, whose advice to register it again bf register then refused. A registered path that became a regular file is absent everywhere, so bf register takes over its name.
  • An empty value, as an unset variable gives, is invalid input for bf collect "", bf run "", --sensor "", --routine "", bf collect SENSOR --since "" and bf build --reproject "": they used to name an unknown program, collect the default window or rebuild the whole cache instead of reprojecting.
  • A zero-length window such as 0d is an invalid period for bf read and --scope alike; the read used to return an empty page while the scope failed with since must be earlier than until. bf validate reports a link to such a page as an unresolved BF target.
  • bf mcp no longer writes the MCP SDK's log line for each rejected or failed tool call to stderr; the reply carries the error.
  • Excerpts of a large passage holding a few characters that folding changes, such as ² or a no-break space, are computed faster.

Documentation

  • The four-tool guide and the watch example give the working checkout command, uv run --project CHECKOUT bf inside a copied brain; the README's four-tool reply names what it trims.

Added

  • scripts/verify-release.sh X.Y.Z verifies a published release in one command: the annotated tag on origin, a non-draft GitHub release holding exactly the wheel and source archive, PyPI digests matching them, their build attestations and a clean install of that wheel that initializes, validates, searches and reads a brain. The release checklist now gives the commands for the release worktree, checking tasks with the pinned mise, waiting for CD and syncing the primary checkout, runs the gate with GITHUB_ACTIONS=true and asks for an independent review before committing. Tests cover scripts/verify-release-tag.sh, the guard CD runs before publishing.

Changed

  • Development tools: Python 3.14.8, uv 0.12.23, dprint 0.60.1, lefthook 2.1.16, trivy 0.75.0 and mise 2026.10.0 in the workflows. The package smoke test builds its environments in the disk cache instead of /tmp, and every test subprocess has a timeout.

v18.1.3 - 2026-10-04

A documentation release. The package behavior, the brain format (version: 7), bf.yaml, the reply schemas and the search cache are unchanged; no upgrade step is needed.

Documentation

  • The loop diagram names its five steps (gather, normalize, connect, act and learn), labels the agents' search and read and starts the act arrow from the agents rather than from bf.
  • The brain session diagram shows the agent noting its conclusion in the project note, as its "writes the outcome back" claim says, and the review diagram shows the full bf read project:new-website command.
  • The watch screenshot shows the log path the dashboard prints, logs/unavailable.log, instead of an outdated state-directory path, and the README shows it at the same width as the other diagrams.
  • The logo's title and description describe the picture instead of design notes.

v18.1.2 - 2026-10-04

A documentation release. The brain format (version: 7), bf.yaml, the reply schemas and the search cache are unchanged. The packaged bf-setup skill only changes its Claude Code guidance: run bf skills DIR to refresh installed copies.

Documentation

  • The README presents BF as a second brain on your own computer: agents opened in a brain start from your projects and evidence instead of a blank session. Four diagrams show the gather-to-learn loop, a blank session beside a brain session, how field mappings turn four tools' names into one knowledge graph and how a project is flagged for review when its linked evidence changes. New sections cover the questions a brain answers and the tools behind them, the sources one brain can gather, bf.yaml as the coordination hub, personal and team brains shared through Git and the trade-offs: local execution, provider setup and the agent harness's data terms. It also points agents to llms.txt.
  • Claude Code 2.1.277+ loads a brain's AGENTS.md on its own, so the guides no longer ask for a CLAUDE.md holding @AGENTS.md; the agent guide keeps that import as the fallback for older versions, and an existing brain may keep its CLAUDE.md.

Changed

  • The release scripts carry a .sh extension: scripts/push-and-tag.sh, scripts/release-notes.sh and scripts/verify-release-tag.sh.

v18.1.1 - 2026-10-03

A documentation release. The package, the brain format (version: 7), bf.yaml, the reply schemas and the search cache are unchanged; no upgrade step is needed.

Documentation

  • The documentation site gains offline search (Ctrl+K or /), header tabs for Start here, Guides, Reference and Privacy and legal, page icons, a light, dark and system color toggle, GitHub repository and edit links, and a footer with privacy, notices and license links. The changelog and license are site pages, so every navigation entry stays on the site. Search keeps the site free of accessibility violations in an axe-core audit: the closed dialog leaves the tab order, its controls are named and its result paths meet contrast.
  • Agents can read the guides as Markdown through llms.txt, which summarizes each guide, llms-full.txt or index.md beside each page, which each guide's HTML advertises as its Markdown alternate. The overview now links every guide.

v18.1.0 - 2026-10-03

A review release of fixes. The brain format (version: 7), bf.yaml and the reply schemas are unchanged; listed fields are bounded, and the search cache rebuilds once. Upgrade with uv tool upgrade brain-framework (or update a brain's pin), then run bf skills DIR.

Fixed

  • bf skills --force keeps a file you edited that the new version no longer ships, as it keeps every other file BF does not ship, instead of deleting it.
  • A record commit whose completion marker landed before a final directory sync failed reports success instead of "nothing was written".
  • A registered brain whose folder exists but cannot be reached fails with cannot reach the brain registered as NAME instead of advice to register it again, which bf register then refused.
  • bf run --hook "", as an unset variable gives it, is invalid input instead of running the first argument as a routine; bf status --check --watch names --watch in its invalid-input line.
  • bf eval --baseline=~/eval.json expands ~, like every other path option. Without a home directory, commands fail with one bf: line naming HOME instead of a traceback.
  • bf schedule installs systemd units below XDG_CONFIG_HOME when it is set, where the user manager reads them.
  • bf watch runs updates for a pip install --user installation, and bf watch --json reports unavailable desktop alerts with a bf: line on stderr instead of a JSON object.
  • A claim footnote's label can no longer change a note's structure: [^```] or [^<pre>] used to open a block that hid the sections and links after it. Links in a footnote's continuation paragraph, indented by four spaces or a tab, now support the section that cites it, and labels match regardless of case, as on GitHub.
  • A copied document whose headings repeat an explicit anchor, or whose title's slug another heading anchors, stays searchable: the later heading takes a generated slug. OKF notes still fail bf validate.
  • An OKF sources entry whose ?rel= names an undeclared relation still cites its target in reads; bf validate still reports the relation.
  • Validation messages count only the distinct problems they do not name.
  • The example sensor, routine and hook guides make a downloaded script executable with chmod +x before configuring it as a command, since a download drops the executable bit and bf validate then rejects the program; github-history.md copies from the release tag matching bf --version and says when to raise timeout for a large backfill.
  • Programs no longer inherit ZDOTDIR, PHPRC or PHP_INI_SCAN_DIR, which make zsh or PHP run code before the program starts.
  • The guarded-write helper follows a linked brain folder and links above it, such as /home on Fedora Atomic or /tmp on macOS, as bf does, instead of refusing every absolute path through them; links inside the brain are still refused.
  • bf-use asks before updating the skills for a newer bf, and names the folder to pass to bf skills.
  • Quitting bf watch while an update is still stopping prints bf: stopping the active update; this can take up to 60 seconds instead of leaving a silent terminal.
  • An exact read across several brains returns the brain that holds the ref with a problems entry for another brain that fails, such as on a malformed record of a shared source or a busy writer, instead of failing; when no brain holds it, the failure stands.
  • A page's address as --scope, such as bf://brain/projects, bf://brain/7d or bf://brain/memories/gmail, is invalid input instead of silently matching nothing; use its brain-relative form.
  • Listed fields stop at 2 KiB of JSON per item, in field-name order, so a shared brain declaring hundreds of fields cannot push home, a listing or a note's backlinks past the reply limit; exact reads keep every field.
  • The retrieval reference states that a relation page's total includes the claims of its narrower relations.

v18.0.0 - 2026-10-03

A review release. Review signals now follow upstream changes and the evidence a note cites, the home page and exact reads summarize what needs attention, and every interface reports invalid input the same way. The brain format (version: 7) and bf.yaml are unchanged apart from accepting empty sections; stricter identity rules and bounds are listed below. Reply schemas only gain optional fields, and the search cache rebuilds once.

Breaking changes

  • Invalid input. A usage error prints one stderr line, bf: invalid input: ARGUMENT: reason (see bf COMMAND -h), with exit 2, instead of Typer's usage text and boxed panel; help is unchanged. MCP clients receive the same rejections as invalid input: ARGUMENT: reason naming the tool argument (query, scope, ref or rel), including malformed refs, periods, addresses, scopes and relations that used to return bare messages.
  • Exit codes. These are invalid input (exit 2) instead of failures (exit 1): --rel on a page written with a trailing slash (projects/) or on an existing folder page, --rel with a #section, a section of a page (bf read tasks#x, bf read bf://NAME/7d#x), an invalid period below memories/SOURCE/, a ref that is not UTF-8 and bf watch --poll-interval nan.
  • Empty values. An explicit empty --brain is invalid input instead of falling back to BF_BRAIN, the enclosing brain or every registered brain, and so are an empty PATH for init and register, DIR for skills and schedule --output.
  • Scopes and retrieval cases. A note or page section as --scope, such as projects/atlas.md#next-actions, is invalid input instead of silently matching nothing. bf eval checks expect and forbid refs as bf read checks a ref, so a malformed or unnormalized ref fails the suite instead of a forbid that always passed, and a malformed identity query fails when the suite loads.
  • Identities. An identity holding a Unicode format character (category Cf, such as U+200B, U+200C, U+200D, U+202E, U+FEFF or a soft hyphen) is rejected: two identities that look alike would silently differ. A BF entity, alias or resource must reach its note or record: another brain's namespace, a #section, a note file path, a page address or a SOURCE:ID address fails bf validate, and the note or record leaves the search cache like one with a foreign alias. A malformed BF resource makes its note invalid, like a malformed alias.
  • Bounds. Note frontmatter aliases and links hold at most 1,000 items, like tags and records, and a record's fields at most 1,000 names.
  • Routine replies. bf run rejects a disabled routine before running anything, with a stderr diagnostic and no JSON reply, as it rejects an unknown one. A --dry-run reply no longer names an action path, which the real run picks anew, and reports skipped when the real run would skip.
  • Skills. bf skills reports newer, exits 1 and leaves the folder when a newer bf installed a different copy of a skill, such as a brain's pinned runtime sharing the skills directory; --force installs this version's copy. The packaged skills search, read and check every brain with the installed bf, never through a brain's pinned runtime, which installs and runs code the brain supplies.
  • Heading slugs. A heading whose title spans several lines, or whose {#id} sits inside emphasis, a code span, link text or an escape, gets a new slug, such as #title-line-one-line-two; bf validate reports links to the old one.

Upgrade: run uv tool upgrade brain-framework (or update a brain's pin), then bf skills DIR; the skills now require Brain Framework 18. Drop empty --brain "" arguments from scripts and host configurations, and match the new bf: invalid input: line where a script parsed usage errors. Run bf validate: retype the identities it names for invisible characters or unreachable declarations, point links at renamed heading slugs, and fix a sensor whose records it reports, then collect again, since collection replaces its own records. Re-copy the example git-history.py, github-history.py, local-documents.py, prompt-context.py, session-context.py or weekly-review.py if your brain uses one; remove timeout: 3600 from a github-history.py sensor, and note that git-history.py --skip with any worktree's path now skips its whole repository.

Added

  • Review signals follow evidence that changed: a project linked from a record whose upstream updated is after the note's last edit, such as an issue closed or a pull request merged, needs review, and so does a note citing a record that happened or changed since. The optional newer field names up to five of those items, newest first. A future event counts once it happens, and an edit always clears the flag.
  • Exact reads summarize their note: a section read states the note's title, type, status and date, and a whole read of a project, or of a note with stale_after, carries tasks, next and its review signals.
  • The home page reviews every current project, lists those needing review first, ends near the 32 KiB page budget and reports projects_total. Its actions, changed and upcoming items carry the same review signals, so an action or concept past stale_after shows there, and deprecated notes leave every home section.
  • bf validate warns, without failing, about record links that are neither identities nor URLs and about a temporary file an interrupted write left behind, and checks programs launched through a command, such as [uv, run, ..., sensors/brief.py].
  • bf schedule warns when its interval is at or above the shortest selected refresh; bf status usage windows gain since when heavy use cut them.
  • bf register at a moved brain's new place replaces the entry whose folder no longer exists, or which reaches the same brain through another path, and reports replaced.
  • A Python whose SQLite is older than 3.35 or lacks FTS5 or JSON functions fails with a message naming the requirement and the fix, instead of a misleading cache error.

Fixed

  • Search keeps the English word comment, and a quoted word is always a search term. one, ones, doing, having, ours, yours, theirs and hers drop like other function words: stemming made them match nearly every passage.
  • Note leads and task text keep identifiers such as MAX_FILE_SIZE, deploy_all.sh, 2*3 and link refs such as _drafts/.
  • Reading a record, and its relation page, lists links to its SOURCE:ID from other selected brains, as search did; review signals skip names that several items claim, as backlinks do.
  • A large read's outline lists only the headings inside it, so an H1-only note or a section without subsections fills its first page instead of pointing back to itself. With several brains, pages fit their items within the 32 KiB budget once refs are named by address.
  • A refresh loads bf.yaml once: a bf.yaml briefly invalid during an editor save no longer leaves the files indexed meanwhile skipped until bf build. A killed build's .bf/index.sqlite.new is removed, and each skipped file's error is cut to 1 KiB so a few hostile files cannot push replies past their limit.
  • Parsing a note with many repeated headings, or a long run of [^, takes linear time instead of stalling search, validation and section reads.
  • A heading anchor is read from the heading's source, and generated -N slugs skip explicit anchors. An indented OKF footnote definition keeps its link, which supports the section that first cites it.
  • A link Python cannot parse, such as http://[your-host]/, or a relative link holding a backslash no longer makes a copied document unsearchable; link errors name the line or frontmatter key.
  • bf validate reports each problem of a note once, names where a link problem first occurs, no longer turns links to a note's entity into false "unresolved BF target" problems, and reports a record link whose scheme case differs from its source, such as Mail:m1.
  • A huge hex or octal YAML integer is invalid YAML instead of a crash of every search over the brain and its references. Validation messages name at most five problems, then how many more, and sensor output is checked record by record, so malformed output or frontmatter cannot build megabytes of error text.
  • An unreadable top-level folder is skipped and reported while the rest of the brain answers, Unix sockets are reported as special files, and empty brains:, fields:, sensors:, routines: or watch: sections read as empty.
  • A --brain path without bf.yaml fails naming the selection before anything runs, a registered brain whose folder is absent fails with the registry's message, and bf register keeps a name whose folder exists but cannot be reached.
  • A window collection that ends before it runs, such as a backfill chunk, extends coverage without counting as a fresh collection. A log routine is no longer killed past max_bytes; its log keeps the last 256 KiB.
  • A program ended by a signal fails with program was killed by SIGKILL instead of exited with status -9. An action folder left with only a killed write's temporary file no longer makes the routine skip for the day.
  • Collection checks projected records against the record bounds and validates each record once.
  • bf schedule previews copy from ~/.config/bf-schedules, where its warning suggests writing, and --output DIR no longer fails when DIR holds the state directory.
  • A disabled program fails with sensor NAME is disabled in bf.yaml; set enabled: true to run it instead of suggesting its own name. bf run --hook EVENT reads standard input only when a routine lists the hook, so an unlisted hook succeeds at once in agent shells.
  • Diagnostics and MCP error text escape control and format characters from file names and keys, and replies escape every Unicode format character, so a shared brain can neither send terminal escape sequences nor hide text. MCP error text hides brain roots, the registry and the home directory.
  • A closed or full standard output fails with one line and exit 1 instead of a traceback or exit 120; closing the terminal under bf watch exits 130.
  • bf watch shows bf's own diagnostic when bf.yaml becomes invalid or drops a selected program, notifications: success and all alert while another program keeps failing, and the details panel shows local time.
  • bf skills finishes an interrupted install or update instead of reporting its own partial copy as unmanaged or as your edits.
  • MCP integer arguments accept integral floats such as 5.0; tags/LABEL/ and bf eval --path evals/ accept a trailing slash; default brain names fold accents (Équipe produit becomes equipe-produit).
  • The example git-history.py collects repositories whose scanned checkouts are only worktrees, once each; github-history.py pages by modification time and reads merge state from the issue listing, one request per 100 items instead of one more per pull request; local-documents.py skips Office owner files; the example hooks and weekly review name items by bf:// address across brains.
  • The skills run helpers with an installed Python 3.14 when python3 is older (uv python find --system --no-config --no-project 3.14), never through uv run, which adopts a .venv above the working directory, and they review a brain's .venv/, uv.toml and .python-version before running its pin. The guarded-write helper names its temporary file like bf's own writes.

Changed

  • bf mcp validates its brain selection at startup, then resolves it again for each call, so registry changes need no restart.
  • Faster: a full build creates its schema in one transaction, a search after an edit scans the brain once, problem checks use an index, record commits count files without a stat each, home computes review signals in one query, and validation parses each note once.
  • Identity search no longer repeats an untyped link beside a typed one from the same origin; usage history keeps 30 days; help and errors name arguments QUERY, REF, SENSOR, PATH and DIR.
  • The contract gate checks configuration schemas for narrowing and reply schemas from the reader's side; mise run check validates the changelog, and a hung test ends the run after 300 seconds.
  • The documentation, skills and examples describe this release: identity rules, review signals, invalid-input lines, the pinned-runtime review and the new limits. Reference pages own each contract and guides link to them.

v17.0.0 - 2026-10-02

A review release. The brain format (version: 7), bf.yaml and the search and read reply schemas are unchanged. The command line is stricter, so input that was silently misread now fails, and failures that took down a whole brain are now contained.

Breaking changes

  • Routine input. bf run ROUTINE passes piped input only with --stdin. It used to read any open standard input and failed after 10 seconds in agent shells that hold it open. bf run --hook EVENT still passes what Git pipes, such as the refs of a push.
  • Repeated options. An option that takes one value may appear once: bf search launch --scope projects --scope concepts, a second --brain, --rel or --hook exits 2 naming it. The last value used to win, so the answer silently ignored the others. --sensor and --routine still repeat to select several programs.
  • Routine arguments. bf run rejects an unknown option before --, such as --dryrun, with exit 2. It used to pass it to the routine, which then ran for real.
  • Read relations. bf read PAGE --rel RELATION is invalid input (exit 2), as a page is recognizable before any brain is read.
  • MCP arguments. Tool arguments are validated strictly: a limit of true or "5" is an error instead of a coerced value.
  • Retrieval cases. A plain expect or forbid ref in bf eval names a file of the evaluated brain. A related brain's note at the same path used to satisfy it; name that note by its bf://NAME/... address.

Upgrade: run uv tool upgrade brain-framework (or update a brain's pin), then bf skills DIR; the skills now require Brain Framework 17. Add --stdin where a script pipes input into a direct bf run ROUTINE, and put -- before routine arguments that start with a dash. Re-copy the example git-history.py, prompt-context.py, session-context.py or weekly-review.py if your brain uses one. Then run bf validate and bf eval.

Added

  • bf status lists each brain's attention: the scheduled programs that failed or are overdue or never succeeded, as the home page does. A brain reported "healthy": false with "problems": [] and left the reason to a scan of every source and routine.

Fixed

  • Search keeps a function word written as an acronym. EU AI Act searched only act, and AI, IT or US dropped from any query, because they spell French or English function words. Lowercase function words and the habitual AND and OR still drop. The example prompt-context.py hook mirrors the rule.
  • Searching a note's title finds the note before a section that mentions it: a section's note title and parents rank below a heading. Atlas next actions still finds that section.
  • One unreadable folder, such as a source restored with another owner, no longer fails every search, status, validation and build of the brain: it is skipped and reported, and bf validate names it as an unreadable folder.
  • Collection stops below the 100,000-entry scan limit of its source and fails like any other collection, so a growing window source can no longer make every search and read of the brain fail. A commit changing exactly as many records as the limit no longer leaves a pending transaction behind.
  • An action routine's relative links and headings are checked before writing, like its metadata and relations: an action that bf validate rejects, such as one linking projects/x.md instead of ../../projects/x.md, is never written.
  • bf validate reports every problem of a note, and links to a note it cannot parse are no longer reported as unresolved: one invalid date or undeclared relation hid the note's other problems and broke the links of other notes.
  • A far-future stale_after, such as 9999-12-31T23:00:00Z, no longer crashes the home and projects pages in timezones east of UTC.
  • bf mcp stops at once on SIGTERM or Ctrl-C with exit 130, instead of waiting for another input line.
  • Reading a note alias named like a source, such as jira:ATL beside a jira source, no longer fails on another malformed record of that source or while a writer holds the brain.
  • Filesystems reporting 64-bit hashed inode numbers, such as mergerfs, no longer fail every cache refresh with a traceback.
  • A query starting with bf:, such as bf: how to configure sensors, searches its words instead of failing as an invalid address.
  • The example git-history.py skips linked worktrees, which collected their repository's whole history again; records already collected stay. The example routine and session hook read a task's Markdown link as its label.
  • The meeting-notes recipe has the agent draft GitHub issues for review instead of creating them from calendar text that anyone who invites you can write.

Changed

  • Packaged skills no longer carry metadata.version, so a release changes a skill's files only when its content changes; .bf-skill.json still records the installing version.
  • Adding broader or targets to a relation no longer rebuilds the search cache: relation pages read broader at query time, and collection and validation check targets.
  • The schedule preview suggests --output ~/.config/bf-schedules, outside the brain, like the documentation: the generated files hold this machine's paths.
  • The documentation states exact reads across brains as they behave: while a brain is unavailable, a found ref returns with a problems entry and a missing one fails as incomplete.

v16.1.1 - 2026-09-29

A patch release from a review of 16.1.0: no format or reply change. Upgrade with uv tool upgrade brain-framework, then bf skills DIR to update unedited skills.

Fixed

  • A declared number field set to .nan or .inf in a note's frontmatter passed bf validate and then failed every search or read reply listing the note with a traceback. bf validate now reports the frontmatter as invalid, and retrieval skips the note with a problem like any other invalid note.
  • A path starting with an unknown ~user, such as bf search --brain ~typo/brain, bf init, bf register, bf skills or bf schedule --output, fails with bf: a path's ~ or ~user home directory cannot be resolved instead of a traceback.
  • An exact read of two records that name each other as aliases no longer recurses when both files disappear between the cache check and the read.
  • bf skills treats a file you added at a path a newer version starts shipping as modified and keeps it, instead of overwriting it.
  • A routine whose action was written but whose run history could not be saved reports wrote PATH but local run history could not be saved instead of claiming no action was written.

v16.1.0 - 2026-09-29

A review release: no format or reply change is required. It fixes the published reply schemas, which rejected some valid replies, and several failure paths; the skills, examples and documentation are clearer and match the code.

Upgrade: uv tool upgrade brain-framework, then bf skills DIR to update unedited skills. Re-copy the example weekly-review.py, github-history.py, git-history.py or local-documents.py if your brain uses one, and merge the new AGENTS.md guidance as Refresh brain instructions shows. From 15.x, apply the 16.0.1 steps first: bf skills reports skill copies made before 16.0 as unmanaged and leaves them; back up any edits and rerun it with --force.

Fixed

  • The published reply schemas (bf schema --kind read-reply, MCP outputSchema) accept every valid reply: an empty period page, such as bf read 7d in a new brain, matched two page shapes, so MCP clients that validate structured output failed the read. Reply shapes are now alternatives (anyOf), and tests validate every reply against the published schemas too.
  • A record's attributes and fields are returned exactly as stored: replies no longer convert instants inside them to local time or drop an attribute named time beside date.
  • A collection that would change more than 100,000 records fails like any other collection error, recorded in history with its backoff, instead of exiting as invalid input and stopping bf update.
  • An unrecoverable cache error prints the search cache is unavailable; run bf build in the CLI and MCP instead of a traceback.
  • A read waiting for another writer to build a missing cache fails after one wait, naming the writer, instead of retrying for up to six minutes.
  • A program that cannot start is reported as such even when its log cannot be written.
  • YAML files other than UTF-8, such as UTF-16 with a byte-order mark, are rejected naming the file.
  • bf skills --check reported a skill with a deleted file as current: it is now modified and lists the file under edited; --force restores it.
  • A reply names an unavailable brain once, worded as its pages report it.
  • bf search and bf read reject --offset above 2^53−1 as invalid input (exit 2), like the MCP tools.

Changed

  • bf update --sensor and --routine suggest close names for an unknown program.
  • bf init writes the same AGENTS.md for every brain (bf://NAME/...), so comparing it with a scratch brain shows only template changes. It asks agents to update notes when the user asks or the task authorizes it, and never to edit memories/.
  • Help text states that watch options override bf.yaml, that status --check also fails on programs that never succeeded and which items export --kind identities lists.
  • Skills: helpers run from any directory as python3 "$SKILL_DIR/scripts/NAME.py", and through uv run --project PATH --locked in a brain that pins its runtime. Version checks follow each skill's compatibility instead of a written version. guarded-write.py refuses a symbolic link anywhere in the note's path; run it from inside the brain with a relative path. new-action.py removes what it created when its write fails. bf-maintain explains each bf status field, and duplicated rules now live in one guide each.
  • Examples: weekly-review.py shows times with their offset instead of labelling local times as UTC. github-history.py skips a pull request updated during its run, which the next window collects. git-history.py requires Git 2.37 and says so. local-documents.py keeps the text read before an Office part's element or nesting limit and marks the record partial instead of failing the snapshot. The sensors README adds a Calendar agenda configuration, and example brains ignore logs/.
  • Documentation: Install and update and the four-tool walkthrough move to Start here, Troubleshooting to Reference. The upgrade steps separate the version upgrade from the manual steps of a major release and explain modified and unmanaged skills. The docs now say that overdue means no success within twice the refresh, that a status cache of stale or missing awaits recovery, and that an exact read across brains fails while one of them is unavailable. They also document the state-directory symlink error and the corrected page sizes, log limits and missed-schedule behavior.

Development

  • scripts/push-and-tag X.Y.Z pushes main, waits for CI on that commit and tags it only after success, whatever shell runs it; tests/test_release.py covers the release scripts, and CI validates the release notes on every push.
  • The weekly security workflow also installs the package on the next Python release, and scans through the gate's tasks without caches.
  • Dependency updates: uv 0.12.20, zensical 0.0.66, sse-starlette 3.5.0, pyjwt 2.15.1.

v16.0.1 - 2026-09-29

First published 16.0 release. The v16.0.0 tag stopped at verification before publication and remains unchanged: two cache-recovery tests expected a rebuilt cache to get a new inode number, which Linux CI filesystems reuse; they now check the rebuilt cache's integrity instead.

Brain Framework 16 settles the contract so that later releases only add to it. One brain format number covers bf.yaml and evaluation suites, one word names each concept, dates and datetimes are separate, routines become general brain programs with hooks and logs, the agent skills ship with the package, and published reply schemas accept added fields while a release gate rejects anything else. Search, the graph, validation and interrupted writes are also more robust.

Breaking changes

  • Brain format 7. bf.yaml and every evals/*.yaml declare version: 7. The schema: key of bf.yaml is now fields:: fields: declares shared fields and relations, a sensor's fields: maps them, a record stores them and a note sets them.
  • Dates and datetimes. A note states its updated day as date (2026-09-29), never as the UTC instant of its local midnight; claims, exports and backlinks asserted by notes carry date too, and review_due is a date.
  • OKF review deadline. Note frontmatter review_due and review_after are replaced by OKF's stale_after, an instant with its timezone: a note is due for review once it is stale. Without it, projects fall due 14 days after their last edit, as before; replies name the deadline's origin as review_source: stale_after or modified. An OKF note's resource URI is one of its identities, like an alias. Every datetime in a reply shows the local offset, to the second (2026-09-29T09:00:00+02:00); files, run history and the cache keep UTC.
  • Routines. A routine is any deterministic brain program declared under routines:. output: log (the default) keeps its stdout in its log; output: action turns its Markdown into a dated action as before. hooks: [pre-push] lets bf run --hook pre-push run it, and bf run ROUTINE [ARGS]... runs one now; both pass arguments and piped input through. Action folders written by routines end in an 8-character suffix.
  • Logs. Sensors and routines log each run to logs/NAME.log in the brain, newest last and bounded to 1 MiB; bf init ignores /logs/ in Git and retrieval never reads it. Status and errors name these brain-relative logs.
  • Freshness. A scheduled program late by more than twice its refresh is overdue (was stale); status no longer repeats it as a stale boolean. stale now only marks results served from a cache a writer is updating. Status reports that cache as busy, and an interrupted transaction as pending_transaction with its recovery command, instead of failing.
  • Search. Quoted phrases and word* prefixes are honored; results matching more of the query rank higher; tags rank like headings; a nested section's title reads Note — Parent — Child; equal scores list the newest first. Replies add unmatched (words found nowhere) and sections (other matching sections of a note). Periods accept 2026-09-21..2026-09-25 as a scope and a page. The search cache rebuilds itself once.
  • Reads. A note above 32 KiB opens with its outline, graph context and first 4 KiB; read its sections by ref or follow next_offset. Task text keeps its links as [label](ref) with brain-relative refs. Listed items and backlink previews show declared single-value fields, such as a status, and backlink previews add an excerpt of at most 160 characters. Outgoing claims keep up to 20 per relation and 50 in all, so a crowded relation never hides another.
  • Export. bf export prints edges; bf export --kind identities lists each note or record that declares names beyond its own address, with every name it answers to. The edges positional argument is gone.
  • Skills. Seven skills become three and ship inside the package: bf-use (find, write and track work), bf-setup (onboarding, discovery and imports) and bf-maintain (integrations and operations). bf skills DIR installs or updates them without overwriting edited copies; --check reports drift. Helper scripts use paths relative to their skill.
  • Initialization. bf init no longer creates tests/, --full no longer creates settings/, and the generated AGENTS.md teaches the new search syntax, date/time and bf run.
  • Limits. Continuation offsets stop at 2^53−1, the largest integer every JSON client represents exactly.

Upgrade: stop the watcher, rename schema: to fields: and set version: 7 in bf.yaml and each evals/*.yaml, replace note review_due/review_after with stale_after, add output: action to routines that write actions, add /logs/ to .gitignore, replace bf export edges with bf export, read freshness: overdue and a note's date in scripts, reinstall skills with bf skills DIR after removing bf-learn, bf-action, bf-scan and bf-import, regenerate native schedules with bf schedule, then run bf validate and bf eval.

Added

  • bf run and routine hooks for Git and other event hooks; bf update names the manual programs it skipped.
  • Typed note claims: an OKF note's fields: sets declared fields, such as owner: [person:email/bob@example.test], validated like a record's.
  • bf skills DIR [--check] [--force].
  • bf export --kind identities.
  • Published reply schemas accept added fields, and tests/contract/ keeps each schema of the major release: any other change fails the gate.
  • MCP tools publish their reply schemas as outputSchema; errors name the search and read tools.
  • bf validate checks that each enabled program in sensors/ or routines/ is an executable file, that declared relations are not written at the top level of frontmatter, and that ?rel= appears only on bf:// links.
  • Lookup errors name what exists: a missing section lists the note's sections, and a mistyped page, note, sensor or routine suggests close names.
  • Status reports each source's bytes.

Changed

  • A link naming an undeclared relation keeps its note or record searchable as an untyped link; bf validate names the relation.
  • bf validate accepts a record's provider alias as a link target, checks a relation's targets against every identity the target's owner declares, and accepts OKF sources whose resource describes a population.
  • Record commits back up replaced files with hard links and roll back only the files they changed, so an interrupted commit recovers in about a second; commits need about half the fsyncs.
  • Exact reads during a commit return within about 3 seconds, marked stale, instead of waiting up to 2 minutes; a waiting cache rebuild is no longer starved by readers; a damaged cache is discarded and rebuilt; watch and generated schedules allow 60 seconds to stop.
  • bf eval ranks each case among the first 50 results, even below its limit.
  • bf --help lists commands from setup to repair.

Fixed

  • A note dated in a timezone east of UTC no longer shows the previous day.
  • An unwritable state directory is named, with XDG_STATE_HOME, instead of blaming the brain.
  • A completed commit whose cleanup was interrupted no longer blocks reads.

v15.0.0 - 2026-09-29

Brain Framework 15 reshapes replies for agents: smaller, bounded, described by published JSON Schemas and easier to continue. It ranks sections by their note, collapses duplicate records across sources, pages relationships, adds graph settings and exports, and hardens collection and cache rebuilds. The brain storage format remains version: 6; the search cache rebuilds once after the upgrade.

Breaking changes

  • Watch preferences now live in the optional watch mapping in bf.yaml. settings/watch.yaml is no longer read, and bf schema --kind watch and watch.schema.json are removed; bf schema includes watch preferences. Defaults and CLI precedence are unchanged. All commands loading bf.yaml validate the section, even when CLI options override it.
  • Exact reads above 32 KiB return their note or record text in pages (text or record.text, with offset, next_offset and total_characters; the first page adds the section outline and the graph context) instead of JSON chunk strings. Every exact read adds sha256 (of the whole file) and modified.
  • Backlink groups always name their relation (links for untyped links) and preview their 5 newest items with ref, title, time, kind and source, status or type only. bf read REF --rel ROLE lists a whole group.
  • OKF sources entries now claim the built-in cites relationship instead of an untyped link: backlinks list them under cites. links and cites join tagged-with as reserved schema names.
  • With one selected brain, result and listing entries omit brain and uri; records omit type, which kind already states.
  • Search sources lists the sources of returned records and those needing attention (failed, stale or never), with sources_omitted counting the rest; replies without items and memories scopes keep the full list.
  • Search shows one result per URL when several sources hold it, such as a Drive file and its catalog entry; the result's also lists up to five other refs, as bf:// addresses when several brains are selected. One source's records, such as highlights of one document, stay separate.
  • Pages and searches end early near 32 KiB instead of 2 MiB, counting a page's summaries such as changed; follow next_offset. Result and listing titles are previews of at most 200 characters ending in …, and home changed and upcoming previews omit excerpts.
  • Period pages (today, 7d, dates) and home upcoming omit records of priority: low sources; sources and activity keep their counts, marked "priority":"low", with the page listing them.
  • Collection rejects a record whose fields other than text exceed 2 MiB serialized, and bf validate reports such stored files, so every record reads back; keep bulky content in text.
  • bf init writes a much shorter AGENTS.md (about 280 words instead of about 960); authoring rules live in the bf-learn skill.
  • The bf-action handoff check reports characters instead of reply_characters.

Manual upgrade:

  1. Stop the watcher, move the keys from settings/watch.yaml beneath watch: in your existing bf.yaml (indent them two spaces), remove the old file, run bf validate and restart the watcher. Remove the empty settings/ directory only if nothing else uses it. Brains without custom watch preferences need no change.
  2. Rename any schema field named links or cites, then run bf validate.
  3. Update scripts that parse replies: concatenate exact-read text pages while next_offset is present and require an unchanged sha256; list a backlink group with bf read REF --rel ROLE and explain a link with bf search 'IDENTITY'; read OKF sources under cites; build bf://NAME/REF from the reply's brain when you need an address with one brain selected; follow also where you expected every copy of a URL; read memories/SOURCE/PERIOD to list a low-priority source's records. Validate parsed replies with bf schema --kind search-reply and bf schema --kind read-reply.
  4. Re-copy the upstream bf-use, bf-learn and bf-action skills and any copied hook (examples/hooks/session-context.py), which read the new replies.
  5. Optionally compare your brain's AGENTS.md with a scratch bf init and keep what your brain still needs.
# https://fmind.github.io/brain-framework/docs/schedule/#watch-preferences
watch:
  interval: 300
  notifications: off

Added

  • Role pages: bf read REF --rel ROLE [--offset N] and MCP read(ref, rel, offset) list every item linking to a note, record or identity through one relationship, 50 per page; an undeclared role exits 2 naming the declared ones, and a bf:// address with an undeclared ?rel= reports a problem.
  • broader: ROLE on a relation field: the parent's role page also lists its narrower roles' items, each keeping its own relation.
  • targets: [PREFIX, …] on a relation field: collection rejects mapped values and typed record links outside them without changing evidence, and bf validate reports stored records and note links outside them.
  • bf validate lists non-failing warnings for identities that differ only by letter case; bf status lists warnings for a record source or authored folder above 80% of the scan limit. Neither changes the exit code.
  • bf build --reproject SENSOR [--dry-run] re-applies a sensor's current mappings to its stored records without running it or removing records.
  • bf export edges prints every claim of the selected brains as JSON Lines, offline, for DuckDB or networkx.
  • sensors.NAME.priority: normal|low in bf.yaml: low ranks a source at half weight in search and keeps its records out of period and home lists; applied at query time without bf build.
  • bf eval reports each search case's rank and the run's mrr; bf eval --baseline FILE lists regressions and improvements against a saved reply.
  • Published JSON Schemas for bf search and bf read replies: bf schema --kind search-reply|read-reply, docs/search-reply.schema.json and docs/read-reply.schema.json. The test suite validates every reply against them.
  • Typed claims carry the asserting item's time.
  • MCP tools gain titles, and the server sends a title and instructions with the orient, find, verify and answer loop, role pages and completeness checks.
  • Opt-in examples/hooks/prompt-context.py for Claude Code and Codex lists up to three matching note refs per prompt, counts records without quoting them, and stays silent after 2 seconds or on incomplete retrieval.
  • The bf-learn scripts/guarded-write.py helper replaces a note from stdin only while it still has the sha256 of the agent's read.
  • Add an explicit f refresh shortcut to bf watch (u remains an alias): reload configured sources and check due work now, retaining one follow-up request during an active update while respecting pause, selectors and retry timing.
  • Add a reviewed GitHub history sensor and backfill walkthrough: selected-branch commits (one year of main by example), all-age issues and pull requests, incremental refresh and bounded pagination that preserves evidence on failure.

Changed

  • The 100,000-entry scan limit applies per source directory under memories/ and per authored folder, and its error names the crowded directory with a remedy.
  • bf build and automatic rebuilds fill .bf/index.sqlite.new beside the live cache and replace it atomically: searches and collections continue meanwhile, and an interrupted build leaves the live cache intact. Readers hold a brief cache lock while connected, so a replaced cache never attaches the new one's write-ahead log.
  • bf update and bf watch recover an interrupted record transaction before refreshing the cache; reads still refuse and name bf build or bf update.
  • Rewording a schema field's description or examples no longer rebuilds the cache; adding broader or targets does.
  • Rolling back an interrupted transaction syncs the source directory once instead of once per record.
  • Mapping errors at collection name the record's zero-based position.
  • The skills describe the new replies, role pages, low-priority sources, rank-aware evaluations, scan warnings and graph settings.
  • Synchronize the project description across the README, documentation, package metadata, CLI help and GitHub About to “🧠 Brain Framework: from information to informed actions.”

Fixed

  • A section now ranks under its note's title as well as its heading, so "Atlas next actions" finds Atlas's own Next actions before other notes that mention Atlas; a query matching only a note's title still returns the whole note.
  • The snapshot shrink guard counted the catalog left by an interrupted commit, letting a truncated listing remove most records; recovery now runs first.
  • A routine retried after its run history failed to save no longer writes a second action for the same day.
  • Temporary .write-* files left by killed writes are removed by the next transaction or recovery of their source.
  • Reject ambiguous GitHub pagination and PR details that move outside the requested window, preserving saved evidence instead of accepting an incomplete or out-of-scope collection.

v14.0.0 - 2026-09-28

Brain Framework 14 is the first stable release. It settles the brain format, the CLI and MCP reply contracts and the execution boundaries so that later releases can extend them without breaking them. Collection only runs in one explicitly selected brain, stored evidence resists partial provider failures, retrieval replies share one shape, and the guides, skills and examples are tested against the release they document.

Breaking changes

  • Formats: bf.yaml (format 6) and evaluation suites (format 5) require version. YAML follows the 1.2 core schema: yes, no, on and off are strings, 017 is decimal and 1:30 is text.
  • Notes: in projects, concepts and ACTION.md notes, only deprecated closes a note: it ranks last and leaves home, tasks and review reminders. Aliases must be namespaced identities such as repo:github.com/owner/name, titles are limited to 4,096 characters, and page paths (home, folder roots, tasks, periods, tags/*, memories/*) cannot be entities or aliases. Other Markdown, such as action inputs and outputs, is ordinary: only valid title, type, status, updated, summary and description apply, and it declares no entity, alias, tag, frontmatter link or review date.
  • Links and identities: body links keep their written form, so BF links must percent-encode spaces and %. A note alias equal to a record ref is an ambiguous identity, an entity or alias whose first path segment contains : is invalid, tagged-with is reserved for tag membership, and periods accept ASCII digits only. bf://NAME/source:id always names a record.
  • Records: aliases must be namespaced identities, ids must fit a BF address (7,988 characters once percent-encoded), URLs are limited to 8,192 characters without control characters, and lone surrogates are rejected. bf validate reports stray files and hidden .json files under memories/.
  • Selection and execution: a bare --brain NAME or BF_BRAIN=NAME resolves through your registry first and fails when the enclosing brain or its references claim that name for another directory. update, collect, watch and schedule act on exactly one brain (--brain, BF_BRAIN or the enclosing brain) and never fall back to all registered brains; outside a brain they fail. For them, a name must be registered or be the enclosing brain's own: a referenced brain or a directory below the working directory fails with neither registered nor the enclosing brain. An enclosing bf.yaml must be a regular file owned by you, and the user registry ~/.config/bf/config.yaml must be a regular file. Program executables must be bare command names or normalized sensors/ or routines/ paths.
  • Collection: a snapshot that would remove more than half of an existing catalog and more than 10 records fails without changing evidence, like an empty snapshot; bf collect SENSOR --allow-removal accepts one such run. Failed sensors and routines retry after 1, 2, 4… minutes, capped at their refresh, instead of every cycle; bf collect retries a sensor at once. A concurrent bf update of the same brain waits up to 10 minutes before failing, and a program whose background process keeps its stdout open fails one second after it exits.
  • Replies: problems is always a list of objects with error and optional brain and file. bf build and bf update report skipped files as skipped and exit 1 when files were skipped; bf status reports its cache as cache, and its source and routine entries use state, last_collected, window, last_run, last_success, failed, failures and log instead of the removed enabled, configured, run, success, start and end keys. bf update replies with one brain object whose sensors and routines lists are always present. Search no longer returns more; next_offset alone signals continuation. All instants are canonical UTC ending in Z; absent ones are omitted, except in bf watch --json rows, where success and next_due are then empty strings. A source without dated records has no latest. bf watch --json no longer emits observing.
  • Reads and searches: exact replies longer than 65,536 characters are JSON chunks from offset 0, and a non-zero offset on a smaller reply fails. The MCP read tool has no brain argument; use a bf://NAME/... address. Both MCP tools reject unknown arguments, such as that brain or a misspelled scope, instead of ignoring them. Identity scopes include the identity's owning note. An unknown memories/SOURCE scope is not found, and a segment below it other than a period, undated or a record file is invalid. Malformed refs, bf://NAME without its trailing slash and queries without any word exit 2, including a search query shaped like a malformed BF address. Retrieval needs write access to .bf and rejects a .bf that is a symlink, not a directory or owned by someone else.
  • Evaluation: bf eval validates every case while loading, including scopes (a 0d window is invalid), read refs, bf:// addresses and query words, and fails the whole run on an invalid suite.
  • Scheduling: a relative bf schedule --output resolves against the brain, and generated systemd units no longer cap the whole update at 45 minutes.
  • Removed legacy: JSON Lines record storage handling, the active, paused, blocked, done and archived statuses, the /logs/ entry in new brains' .gitignore and the hand-written systemd example.

Upgrade from 13: stop collection and back up the brain, then upgrade. Declare version: 6 in bf.yaml and version: 5 in each evals/*.yaml, and write true or false where a YAML 1.1 yes, no, on or off meant a boolean. Replace the note statuses active, paused, blocked, done and archived with draft, stable or deprecated, namespace aliases such as repo:github.com/owner/name, percent-encode spaces and % in BF links, and make each program's executable a bare command or a normalized sensors/ or routines/ path. Pass --brain to update, collect, watch and schedule outside a brain, and regenerate native schedules with bf schedule. Update reply parsers for problems objects, the renamed bf status keys and the removed search more, and MCP read calls to use a bf://NAME/... address instead of brain. Then run bf validate and bf eval and fix what they report.

Added

  • Sort bf watch and bf status --watch by name, last success, returned items, state, next due time, changes, duration, output bytes, refresh interval or kind, with reverse order, first/last navigation, an item-count column and a keyboard guide. JSON watch rows expose records, the last successful sensor run's returned count.
  • bf schema --kind exports offline editor schemas for brain, watch, registry and evaluation files, generated and checked from the runtime models.
  • bf eval assembles chunked exact reads and verifies their digest before checking read cases.
  • bf validate names the file of every problem, flags capped lists with problems_truncated and unresolved_truncated, checks embedded images and accepts page addresses (home, folders, periods, tags, sources and record files) as link targets. Project notes resolve a leading / from projects/, as concepts already did.
  • Registered brains that are absent on this machine appear in search and read problems, and bf status --check fails on them and on brains: references that retrieval cannot include.
  • memories/SOURCE/undated is a search scope, like the page of that source's undated records.
  • The bf-learn evidence helper reads and assembles chunked exact replies with read REF --brain BRAIN.
  • Documentation for shell completion, refreshing a brain's AGENTS.md from the installed template, and every enforced limit.
  • The package declares Development Status :: 5 - Production/Stable.

Changed

  • Search matches words in one Unicode compatibility form, so ß, ligatures, full-width letters and decomposed accents are found, while excerpts keep the source's characters. The search cache rebuilds itself.
  • Collection parses only the record files it replaces or removes and syncs each directory once per transaction, and exact reads of absent records reuse a ready cache.
  • Every lock follows the physical brain directory, so different paths to one brain share update, watch and program locks, and a busy program names itself.
  • Sensors and routines receive BF_BRAIN set to the executing brain, so nested bf calls read that brain.
  • For scheduled window sensors, a manual bf collect after a pause no longer moves the resume point past the uncollected gap.
  • The AGENTS.md written by bf init states execution authority, one-brain selection, incomplete-result boundaries, deprecated-only closing, chunked reads and multi-brain uri reads. New brains' starter evaluation reads bf://NAME/concepts/welcome.md, and new bf.yaml files omit redundant defaults.
  • The watch dashboard keeps the selected row, program states and failure details visible from 80 columns, shows the sort field and direction in the panel title, applies batched keys in order and redraws only on change or once a second.
  • Documentation is organized around first use, everyday tasks, collection and reference. Guides install with uv tool install --python 3.14 brain-framework, copy examples from the release tag matching bf --version, and name action folders YYYY-MM-DD_topic-SUFFIX; tests/test_guides.py runs the getting-started guide and checks every reply it shows.
  • Skills describe the 14 contracts, use ## Decision {#decision} in project and action templates, and are tested for the commands, options, anchors and Python 3.11 standard library they rely on.
  • Example sensors stay within the record limits, map provider fields explicitly and print content-free reasons for their failures. The Calendar sensor keeps cancelled events as Cancelled: SUMMARY at their original time, the Git history sensor reads branches, tags, remotes and HEAD and skips unreadable folders, and the team example is a bf and git walkthrough.
  • Ordinary Markdown whose foreign frontmatter is not valid YAML stays searchable without it, a block that never closes is searched as text, and Emacs .#NAME lock files beside authored files are ignored, so none of them fails bf build, bf update or bf validate. A routine that already wrote today's action skips its rerun while that folder holds any file, even an editor lock.
  • Collection replaces, or a snapshot removes, a stored record file that holds its own id but breaks the current record rules, such as a display-name alias or an over-long title; misnamed files still fail it.
  • Diagnostics name what to fix: duplicate YAML keys give their position, unknown watch settings and undeclared field mappings name their key, search errors name QUERY or --scope, bf register names a missing directory or bf.yaml, unavailable or unloadable brains are named once by their registered or brains: name, and bf init asks for --name when the directory name cannot form one.
  • bf schedule without --output warns that its install commands name files it did not write. When an update fails only because the search cache skipped files, bf watch says so and sends no collection-failure alert. JSON watch rows are documented key by key.
  • The bf-action helper accepts a symlinked brain root, the bookmark inventory helper distinguishes a reached limit from an unsupported export, and example sensors name the repository, folder or limit behind a failure; the Calendar sensor keeps very large events within the 1,000-link record bound. Guides write GitHub identities in lowercase and install skills and the watch demo from the release tag.
  • Development uses uv run --locked, hermetic UTC tests under pytest 9 strict mode, a 95% branch-coverage floor, generated third-party notices (mise run generate:notices) and a dashboard screenshot rendered from the fictional demo (mise run generate:screenshot), each checked by the gate. CI runs shared checks once and tests on all four platforms; the weekly security workflow also installs the package with the newest allowed dependencies, and the package check completes an MCP stdio handshake with the installed package.

Fixed

  • Body links to identities or records with accents, spaces or % now match their frontmatter form in backlinks, reads, scopes and validation.
  • Removing or retyping a schema field no longer hides the stored records that carry it.
  • A note whose H1 is followed by a --- rule, or that starts with a byte order mark, keeps its frontmatter; link validation compares exact file names on case-insensitive disks; lead and anchor parsing is linear on adversarial notes.
  • Period and source pages report each problem once; previews order ties like their continuations; deep search offsets no longer compute skipped excerpts; reading a typed link to an unowned identity works.
  • bf eval names the suite and case of an invalid case before any retrieval; JSON replies are UTF-8 whatever the locale; a non-UTF-8 note is reported as such.
  • Collection survives files that vanish during a scan, names linked intermediate folders and the directory that exceeds the depth limit, and ignores empty or relative XDG_* values.
  • A successful sensor whose background helper inherits stderr no longer times out; clock corrections no longer delay scheduled programs; malformed usage events no longer interrupt status.
  • The watch dashboard no longer hides the selected row when programs outnumber the screen or truncates states and counts on 80–128 column terminals.
  • The example hook, routine and sensors run on Python 3.11 again; example Calendar and Drive sensors reject malformed continuation tokens; the local documents sensor names unreadable, damaged or encrypted files.
  • A file or folder name that is not valid UTF-8, such as a Latin-1 name from an old archive, or that holds a backslash, is reported as file name is not valid UTF-8 or contains a backslash; rename it instead of failing search, read, status, build and validate for the whole brain.
  • Documentation navigation and wide tables are keyboard accessible, and release notes reject empty or duplicate sections.
  • Remove a breaking-change bullet copied by mistake into historical release sections.

Security

  • Brain names resolve through the owner's registry before the working directory, an enclosing bf.yaml is trusted only when you own it, and registered brains are selected for retrieval only, so an untrusted checkout cannot run its programs under your name.
  • Program environments also drop SHELLOPTS, BASHOPTS and PS4.
  • The private state root is created with mode 0700; the search cache is opened through one no-follow descriptor, created with mode 0600 and read with SQLite's defensive settings.
  • The example session-context hook never prints collected record refs, and a non-interactive bf status --watch no longer suggests the program-executing bf watch --json.
  • A search cache that bf did not create in place, such as one copied, cloned or extracted with a brain, or one holding triggers or views, is rebuilt from the brain's files, so rows no file supports never reach retrieval. The cache is bound to its file's inode number, which a remount keeps.
  • CLI JSON replies, MCP tool text and bf watch --json rows write DEL and C1 control characters from collected text as JSON escapes, so a terminal cannot act on them; the decoded values are unchanged.
  • Collection errors, run history and bf status no longer quote an undeclared link relation from sensor output.

v13.0.2 - 2026-09-27

First published 13.0.x release. The v13.0.0 and v13.0.1 tags stopped at verification before publication and remain unchanged. CI and scheduled security checks now pin mise 2026.9.15. Terminal-restoration checks exercise line input before comparing all settings, accounting for macOS kernel state while retaining cancellation and restoration assertions.

Brain Framework 13 adds continuous collection, independent record files, complete retrieval and practical workflows from evidence to decisions. Before upgrading from 12, follow the manual steps below on a backed-up copy with collection stopped.

Changed

  • Use the same product description in package metadata, CLI help, the README and documentation: 🧠 AI Brain Factory: from information to informed action. Not for 🐙 mindflayers or 🧟 zombies.

  • Breaking storage change: bf.yaml uses format 6. Each source record lives at memories/SOURCE/SHA256_ID.json, replacing monthly JSONL catalogs. Record IDs and source:id refs stay stable; source pages replace the partitions inventory with paginated records. Retrieval evaluation files remain format 5.

  • Breaking execution change: remove machine collection permissions and sensor content-trust settings. Registration stores names and paths only; explicit collection/update/watch commands run enabled programs in selected roots, never referenced brains. Remove obsolete registry collect fields, sensor trust fields and --collect options. Retrieved content remains untrusted evidence.
  • Breaking note contract: projects, concepts and canonical action ACTION.md notes require OKF structure, a nonempty type, and draft/stable/deprecated lifecycle statuses. Work progress belongs in the body and checkboxes; attachments may remain ordinary Markdown. Routine output is validated before an action is created.
  • Breaking reply contract: remove collect from init, registration and status replies, trust from source metadata, and external from retrieval and evidence-helper replies. Pages include record excerpts consistently; hooks and routines reference collected records without copying their text into authored context.
  • Review reminders use local file modification time, optional review_after days or a review_due date. Replies explain deadlines and newer evidence without claiming a review occurred. File copies/checkouts can reset modification times; explicit deadlines remain portable.
  • Routine actions use unique session suffixes, with daily suppression local to each collecting clone. Shared brains can merge independent records and actions independently; conflicting evidence still requires review.
  • Rewrite guides around a runnable decision example, first sensors, schema mappings, team setup and expected results. Keep the README concise and expand advanced reference pages, privacy boundaries and recovery instructions.
  • Refresh compatible Python dependencies and keep task output concise while retaining diagnostics, security findings, test counts and coverage totals.

Added

  • bf watch follows collection in a full-screen terminal dashboard with keyboard navigation, pause/cancellation and last-run changes. bf status --watch observes without execution; watch --json streams snapshots. A second interactive watcher observes the active collector.
  • Brain-owned settings/watch.yaml controls check/display periods and generic desktop failure/recovery alerts, with optional success notifications and cooldowns.
  • bf schedule generates inspectable systemd, launchd or cron definitions and installation/removal commands without activating them. Repeatable --sensor and --routine options select programs for update, watch and schedule while retaining enabled/refresh rules.
  • Window sensors can use a separate reconcile cadence to revisit older evidence. Collection/status replies report requested windows, reconciliation, elapsed seconds, output bytes and record change counts.
  • bf read tasks lists open checkboxes with source sections, line locations, complete counts and pagination. Weekly reviews summarize tasks without duplicating the original checkboxes. bf://NAME/tasks is reserved for this computed page.
  • Browse tags, follow brain-qualified tag pages and search explicit tag membership with --scope bf://NAME/tags/LABEL. Tags retain their originating notes and use bounded, validated labels.
  • bf-setup, bf-scan and bf-import guide onboarding, explicitly scoped discovery and selected imports. New brains include technical-test and retrieval-evaluation folders with immediately runnable starter cases.
  • Action helpers create independent sessions and check Context/Resume budgets before handoff. Learning guides cover reviews, evidence retention, sharing and conflict resolution.
  • Runnable fictional context-hub, watch and two-contributor examples demonstrate integrations and collaboration. A highlights sensor imports selected passages with URLs, locators and annotations kept separate from source text.
  • Deterministic retrieval evaluations under evals/ join the full gate without models or providers. Package checks exercise wheel and source archives outside the checkout; the manual benchmark validates every measured result.

Fixed

  • Search/listing continuations expose remaining results. Oversized exact replies return lossless JSON chunks with SHA-256 verification; outgoing graph previews mark omitted claims. Empty searches retain failed/never-collected source coverage.
  • Symlinks and special files are skipped and reported without following their targets or hiding other evidence. Incomplete exact reads fail visibly; pending recovery journals still block retrieval until repaired.
  • Invalid YAML identifies the file, line and column without quoting private content. Persisted-record diagnostics redact provider-controlled keys. Status reports a broken brain while retaining reports for other selected roots.
  • Reject unresolved Markdown merge markers and foreign-namespace note aliases during validation/indexing. Preserve source links on every canonical note type and resolve typed links to tag pages correctly.
  • Preserve existing evidence-capture destinations, including dangling symlinks. Session hooks constrain metadata to its owning brain; weekly reviews follow all project continuations and reject incomplete retrieval.
  • Keep Git history collection within selected directories and half-open time windows, including in-window commits with out-of-order dates.
  • Keep starter absent-evidence evaluations stable as notes grow. A malformed optional registry no longer prevents operations on an explicitly selected brain path.
  • Retry failed scheduled programs on the next cycle even when an earlier success is still within its refresh interval. The watch display shows these retries as immediately due.
  • Remove empty code-line links from the documentation's keyboard navigation and accessibility tree.

Manual upgrade from 12

  1. Stop all writers. With version 12, run bf build and bf validate, then back up the whole brain, including ignored memories and attachments. Git alone is not a full backup. Work on a separate copy with version 13 installed in a separate environment.
  2. Convert each nonblank line of memories/SOURCE/*.jsonl into memories/SOURCE/SHA256_ID.json, using the lowercase SHA-256 of the UTF-8 record ID. Preserve every field and reject duplicate IDs, duplicate JSON keys, symlinks and unexpected files. Write to a fresh directory, verify counts, then replace only the copy's memories/; never install partial output. Brains without collected records skip this step.
  3. Set version: 6 in the copied bf.yaml; retrieval suites stay at format 5. Remove sensor trust fields. Explicit collection/update/watch commands now run enabled programs in selected roots; review their configured argv before execution.
  4. Give projects, concepts and canonical action notes a nonempty type and draft, stable or deprecated status. Move work states into the body/task list; preserve OKF sources and verification metadata. Replace aliases claiming another brain's namespace with explicit links.
  5. Back up the user registry, then remove registration collect fields and obsolete --collect host flags. Update installed skills and restart hosts. Reply consumers must follow next_offset, verify/reassemble exact-read chunks and stop expecting removed external, trust, collect or source partitions fields. Routine action paths now include a UUID suffix.
  6. On the copy, run bf build, bf validate, bf eval and technical tests. Compare record counts, search a known decision and read its reason and supporting record. Resolve problems/stale and check source freshness separately before switching collectors/hosts and resuming the authorized schedule. If checks fail, retain the original version-12 runtime and backup together; do not open format-6 data with version 12.

v13.0.1 - 2026-09-27

Unpublished tag. The macOS terminal tests compared a transient kernel flag before processing the next input. See v13.0.2 for the full release notes and portable terminal checks.

v13.0.0 - 2026-09-27

Unpublished tag. See v13.0.2 for the full release notes and corrected CI toolchain.

v12.0.2 - 2026-09-25

First published 12.0.x maintenance release. The v12.0.1 tag stopped at the CI gate before publication and remains unchanged.

A maintenance release for reliable absence checks, documentation and release verification. Brain configuration and retrieval suites remain at version 5; no manual upgrade is needed.

Fixed

  • Schema checks use their own scratch directory, so a concurrent clean documentation build cannot delete their intermediate files.
  • Collection bounds saved coverage by its successful run and the current observation time. A future window left in run history no longer keeps successful scheduled collection permanently stale.
  • A missing read no longer proves absence when a directly referenced brain is unavailable or identity lookup skipped invalid evidence. Retrieval evaluations expecting an empty answer fail visibly until the incomplete scope is repaired.
  • Future-dated notes appear in the home page's upcoming items without displacing recent changes.
  • PDF converter process tests allow interpreter startup on busy machines while still asserting output limits, timeouts and process cleanup.

Changed

  • CI and release publication share the four-platform verification workflow. PyPI and GitHub receive the exact distributions that passed isolated installation tests, with hash comparisons before and after publication.
  • Wheel and source-distribution checks install locked runtime dependencies outside the checkout and exercise initialization, validation, search, exact reads, status and retrieval evaluation.
  • Development tasks and hooks use native tools with separate import sorting and formatting, a configured coverage floor, strict rendered documentation links and disposable schema generation.
  • Refresh the README, documentation overview and branding; clarify which sensor integrations ship as reviewed examples and how to create others.

v12.0.0 - 2026-09-25

Brain Framework 12 ranks notes and records in one search query and answers several times faster; BF links carry only a relationship. On a personal brain of 52,000 records, full cache builds take 13 s instead of 69 s, searches and reads 0.3–0.4 s instead of up to 1.8 s, and the cache shrinks from 357 MB to 239 MB. The bf.yaml and retrieval suite formats stay at version 5, but notes that use removed link attributes or frontmatter fields need the manual upgrade below. The search cache rebuilds automatically.

Changed

  • Breaking: search ranks every item containing any of the words in one BM25 query. The former all-words pass let long records that happened to contain every word fill the results before a note matching most of them; BM25 already ranks fuller matches higher.
  • Only projects, concepts and each action's ACTION.md receive the note ranking boost. concepts/index.md, concepts/log.md and an action's inputs/ and outputs/ rank like evidence.
  • A query shaped like an identity (re:invent, python:3.14) that no item is, names or links to ranks its words instead of returning a silent empty answer; the reply carries "identity": "unknown".
  • Record field values are searchable words without their JSON keys, so a word such as author or kind no longer matches every record with that field.
  • Breaking: a BF link accepts only ?rel=ROLE. The subject, evidence and asserted-by query keys and typed link attributes are removed; a link's subject is its note's entity, otherwise its file, or the collected record. Claims in relations and claims are subject, relation, target and origin: the evidence, asserted_by and attributes members are gone.
  • Breaking: note frontmatter fields no longer declares relationships; it is ordinary frontmatter data.

Fixed

  • Searches no longer compute an excerpt for every matching passage before ranking: only returned passages get one. A four-word query over 50,000 records took 1.9 s in excerpts alone.
  • bf.yaml is parsed once per content within a command, with libyaml when available, instead of several times per search and once per indexed file during a rebuild.
  • The relationship cache no longer copies an eight-column key into each of its indexes.
  • Backlinks, identity searches and project review signals look up links and relationships through indexes instead of scanning every link of the brain for each relationship group.

Upgrade

  1. Find removed link attributes: grep -rnoE 'bf://[^) ]*\?[^) ]*' projects concepts actions | grep -vE '\?rel=[a-z0-9-]+(#[^) ]*)?$'. Keep ?rel=ROLE; move a relationship of another entity into that entity's note, and keep supporting references as ordinary links in the same section.
  2. Find frontmatter relationships: grep -rln '^fields:' projects concepts actions. Replace each identity value with a [label](bf://NAME/path?rel=FIELD) link in the note body.
  3. Consumers of relations[].evidence, asserted_by or attributes read relations[].origin instead.
  4. Run bf validate: remaining removed attributes are reported as invalid BF links.

v11.1.1 - 2026-09-25

First published 11.1 release. The v11.1.0 candidate stopped at the release test gate before publication; its tag remains unchanged.

Brain Framework 11.1 adds optional decision workflows to the agent skills and hardens collection, retrieval and validation after a full review. The brain format is unchanged: bf.yaml and retrieval suites stay at version 5.

Added

  • Decision workflows in the bf-action and bf-learn skills, loaded on demand: a small working context in each action (## Context {#context} of at most 300 words, six refs and 4 KiB, and ## Resume {#resume}), decisions with alternatives and expected outcomes, conditional intentions, explicit unknowns, belief revision with a declared supersedes role, dependency review bounded to two hops and ten dependents, procedures learned from outcomes and reviewed transfer to another brain.
  • skills/bf-learn/scripts/evidence.py, a standard-library helper for Python 3.11 or later: capture retains one exact bf read with its digest and limitations, and compare returns a compact changed, unchanged or unknown verdict for a new read. It reads stdin only and never opens a brain, runs a provider or uses a model.
  • An agent workflows page covers the skills, the everyday loop, resuming actions, decision workflows and evidence captures.
  • The example brain demonstrates a decision review: a fictional policy, a superseded decision, an intention, an unknown, a draft procedure, a prepared transfer and retrieval cases for each.

Changed

  • Search excerpts are one line and start below a section's heading, which the result title already names. The search cache rebuilds automatically.
  • Exact record reads and search coverage report the same freshness as bf status and source pages: a trusted scheduled sensor that never succeeded is never. An unreadable registry grants no trust and leaves freshness unknown.
  • An invalid --scope, --since or --until exits 2 as invalid input, like other command-line errors. A closed terminal (SIGHUP) cancels like SIGTERM and kills running providers; nohup keeps it ignored.
  • bf status --check fails only for scheduled sensors this machine runs, as documented; a failed manual collection is still reported with its log.
  • Registered brains win over a same-named directory below the working directory for --brain NAME.

Fixed

  • The brain writer lock follows the brain directory's device and inode, so bind mounts and differently spelled paths of one brain no longer commit concurrently. Writers must share one private state directory.
  • A state directory below a linked ancestor, such as /home linked to /var/home, no longer breaks every command; the state root itself still may not be a link.
  • A registry entry with a relative path is rejected instead of granting collection trust to whatever that path means in the working directory.
  • Relative PATH entries no longer let a bare command resolve inside the brain, and every PYTHON* variable plus Java, Lua and GCONV_PATH startup variables are removed before a sensor or routine starts. Brain executables may exceed 1 MiB.
  • Invalid sensor output is reported by record position and field name only: keys the provider printed no longer reach errors, logs or run history.
  • An empty snapshot no longer erases a non-empty catalog; the run fails and keeps the records. Delete memories/SOURCE/ to clear a source deliberately.
  • A backfill whose --until lies in the future no longer blocks later scheduled successes: coverage never extends past the run.
  • A routine run skipped because today's action exists no longer loses its window; the next written action covers it.
  • A stored revision claiming a modification after it was first observed, such as a file with a future mtime, no longer freezes a record against newer collections.
  • A note dated 0001-01-01 or 9999-12-31 is invalid instead of crashing searches and pages; a damaged cache reports "run bf build" instead of a traceback.
  • read keeps answering from the other brains when one selected brain has an invalid bf.yaml, and reports it under problems.
  • An alias claimed by several notes no longer merges their backlinks in scoped searches and identity pages.
  • Identity searches include links to a note's sections, like scoped searches and backlinks.
  • Period pages order changed items by modification time across brains.
  • memories/SOURCE/PERIOD for an unknown source is a missing page instead of an empty answer.
  • Reading an identity or an absent source no longer waits for a running writer.
  • A record id too long for a BF address still reads, with its backlinks reported as unavailable.
  • Headings without word characters get an addressable section slug; explicit anchors cannot end in .md; a link to a non-Markdown file whose name contains # validates.
  • The session hook and the weekly review show local dates, and the weekly review writes one paragraph per line and fences record ids so that none can become a link.
  • The exit-code test accepts colored usage errors, as CI terminals print them.

v11.0.0 - 2026-09-25

Brain Framework 11 turns listings into pages, gives actions a resumable page and schedules deterministic routines next to sensors. Search takes words and one scope; everything else is a page that read resolves. The brain format changes: bf.yaml and retrieval suites move to version 5.

Changed

  • bf read without a ref returns the home page: active and blocked projects, the latest actions, notes changed in 7 days, record activity per source in 24 hours, items in the coming 7 days, and scheduled sensors or routines that need attention. Projects are marked for review when their note is older than 14 days or items dated after it link to them (new_links), and show open tasks with the next one.
  • read resolves pages: folders (projects, concepts, actions and subfolders), periods (today, yesterday, YYYY-MM-DD, YYYY-MM, 12h, 7d, 2w) with items modified in the period and each source's share, memories, memories/SOURCE with its partitions, and memories/SOURCE/PERIOD, snapshot or undated. Pages combine the selected brains, stay bounded (50 period or source items, 200 notes per folder, 20 per section) and report totals; bf://NAME/ and bf://NAME/PAGE select one brain.
  • Whole note and record reads include backlinks across the selected brains, grouped by explicit relationship with claim explanations, and claims whose explicit subject is the item. An identity without an owning note reads as a page of what links to it. bf read actions/FOLDER returns ACTION.md with the action's files and the projects it links to.
  • Breaking: bf search QUERY [--scope SCOPE] [--limit N] requires words or an identity. A scope is a folder or file, a period or an identity. --since, --until, --source, --type, --status, --recent, --changed-since, --current, --relation, --target and --subject are removed. MCP search takes query, scope and limit; MCP read accepts an empty ref for the home page.
  • Breaking: bf.yaml version 5 declares routines:, deterministic programs that bf update runs after the sensors with the same trust, process boundary, timeout, output bound and private log. A routine's Markdown is validated and written as actions/YYYY-MM-DD_NAME/ACTION.md; an existing action is never replaced, empty output writes nothing, and a failure writes nothing and keeps it due. Routine names are action slugs distinct from sensor names. bf status reports routines, and --check fails on stale or failed ones.
  • Breaking: retrieval suites use version 5. A case is a search (query, optional scope and limit) or a read (read: a page, note, record or identity), checked with expect, forbid, text and empty; a read that finds nothing is empty.
  • Sensors declare trust: owner for text the brain's owner writes, external (the default) for mail, chat, invitations, issues, feeds and other third-party text. Pages show external records by title and ref without excerpts; searches, exact reads and backlinks label them external; source coverage reports each source's trust, and undeclared historical sources are external.
  • bf status no longer lists review; project review moved to pages. Process errors name the program rather than a collector, since sensors and routines share the runner. The search cache rebuilds automatically.

Added

  • The bf-action skill starts, resumes and closes one action, a single session of work, when the user asks for it; the action template moves there from bf-learn.
  • examples/routines/weekly-review.py renders a weekly review action from bf read pages, with a fake-bf test and a README contract. It names external items by ref only.
  • examples/hooks/session-context.py prints a short context for the current repository at agent session start (project, review signal, next task, linked evidence), and nothing when unavailable.

Fixed

  • A manual backfill that ends before a sensor's recorded coverage no longer replaces that coverage, moves its resume point or marks the sensor fresh; a contiguous backfill extends coverage backwards without counting as a fresh success.

Manual upgrade from 10

  1. Pause scheduled writers. Install the same Brain Framework 11 build for every CLI, MCP host, project environment and scheduled writer, and restart long-running MCP readers.
  2. Change bf.yaml to version: 5. Every sensor is now external by default: add trust: owner to the sensors whose text you write yourself, such as local Git history or your own documents.
  3. Change every suite under evals/ to version: 5. Rewrite cases that used removed fields: a time window or filter becomes a read of a page (7d, 2026-09, projects, memories/SOURCE) or a search scope; a relation, target or subject case becomes read: IDENTITY with expect, and text naming the role when it matters.
  4. Replace scripts, routines, skills and agent instructions that call removed search options: pages for listings and timelines, bf read IDENTITY for backlinks and claims, --scope for bounded searches. Update the AGENTS.md that earlier bf init generated, and reinstall bf-use, bf-learn, bf-maintain and the new bf-action.
  5. Optionally move scheduled review scripts into routines/ and declare them under routines:. Review them like sensors before a trusted machine runs them.
  6. Run bf build, bf validate and bf eval; compare the home, projects and period pages with your previous listings, then resume the scheduled writer.

No migration tooling or legacy command surface is included. Runtime dependencies are unchanged.

v10.0.0 - 2026-09-25

Changed

  • Declare directly related brains in bf.yaml with stable names and relative, absolute or home-relative paths. Search, read, MCP and evaluations include direct references without recursive discovery or required global configuration. Missing references report incomplete scope; conflicting names are excluded.
  • bf init defaults to no global registration or collection trust; explicit --collect opts in. Maintenance commands do not expand references, and references never authorize sensors. Retrieval suites accept qualified BF addresses to distinguish same-named files across brains.
  • Add portable bf://brain/path#section addresses, explicit note entity identities and typed note fields. BF link queries encode declared relationships and attributes, with explicit subject/evidence/attribution and an immutable origin in the derived SQLite projection.
  • Add selected-brain alias federation, incoming target and outgoing subject filters, and relationship explanations through CLI, MCP and retrieval suites. Ambiguous aliases do not merge, external URL queries stay opaque, and foreign links never broaden brain selection.
  • Headings support stable {#anchor} identifiers. Validation checks local BF addresses and reports foreign unresolved targets. New brains include a small relationship vocabulary and agent instructions for links.

  • Brain format 4 restores explicit schema fields in bf.yaml: descriptions, strict types, cardinality, validated examples and typed relationships. Sensors map their record output with JSON Pointers or constants; normalized values are stored in record fields and included in offline search.

  • Typed relationships form a disposable, evidence-backed SQLite graph. CLI, MCP search and retrieval cases accept relation and target together, with exact identities and explicit owner aliases. Schema changes invalidate the cache; record replacement and snapshot deletion remove obsolete edges.
  • bf eval discovers YAML suites recursively under evals/; --path selects a suite or directory. Technical tests remain in tests/. bf init --full creates both folders.
  • Git and Calendar example sensors preserve author/repository and organizer/attendee roles for explicit mapping.

Manual upgrade from 9

  1. Preserve the brain files, configuration, machine state and previous runtime; pause all collection writers and finish or recover pending transactions.
  2. Install the same Brain Framework 10 build for every CLI, MCP host, project environment and scheduled writer. Restart long-running readers after the switch.
  3. Change bf.yaml to version: 4. Declare shared fields under schema and each sensor's fields mapping. Keep provider transformations in sensors; do not infer missing relationships from names or flattened links.
  4. Create evals/, move queries.yaml to evals/retrieval.yaml, change suite version to 4, and update paths in tasks, docs and skills. Multiple named suites may share the directory.
  5. Existing records without normalized fields remain readable. Preserve originals before explicitly backfilling fields from retained structured evidence; unavailable historical roles remain unknown. Mapping changes alone do not rewrite history or contact providers.
  6. Add directly related brains under brains: {team: {path: ../team}} in bf.yaml; ensure target names match. Readers now include these direct references, so review the intended audience. Use paths or local names for discovery. Existing optional registrations remain usable; newly initialized brains need explicit --collect before collection.
  7. Keep the brain name stable across clones; use it as the BF URI authority. Declare roles before adding BF link queries. Give entity notes explicit entity identities and reviewed aliases; do not reinterpret external URL queries or fabricate historical relationships. Refresh separately installed skills and brain agent instructions.
  8. Run bf build, bf validate, technical tests and bf eval; check role-specific queries and exact reads, then resume the scheduled writer. Keep originals until recovery is verified. Rollback requires the prior runtime and original configuration and records, not only a cache rebuild.

No migration tooling or legacy command surface is included. Runtime dependencies are unchanged.

v9.2.0 - 2026-09-24

Brain Framework 9.2 starts new brains smaller, checks action folders and repairs the PyPI project page. The brain format is unchanged: existing version 3 brains need no conversion.

Added

  • bf init --full also creates the optional versioned folders: memories/, assets/, sensors/, routines/, settings/, skills/ and tests/.
  • bf validate reports action folders that are not named YYYY-MM-DD_slug or lack ACTION.md. Rename such folders with git mv; validation then reports any links to update.
  • Document the optional brain folders: versioned assets/ for media that notes link to, and the unversioned inputs/, originals/ and logs/ that new brains already ignore.

Changed

  • bf init creates only projects/, concepts/ and actions/; other folders appear when first needed.
  • bf register keeps the registry's leading comment lines when it rewrites the file.
  • Search, MCP and retrieval cases resolve relative times in one place. An invalid search time exits 2 as invalid input, and an invalid retrieval case names the case.
  • bf status builds its report in the health service; the CLI stays a thin adapter.
  • PyPI publication accepts deployments only from v* tags.

Fixed

  • README links are absolute, so the PyPI project page reaches the documentation, skills and examples; a test keeps every link resolvable offline.
  • bf eval names a missing queries.yaml instead of reporting an inaccessible file.
  • Claude Code discovers the repository's bf-contribute skill through .claude/skills.

v9.1.0 - 2026-09-24

Brain Framework 9.1 prepares team deployments. The brain format is unchanged: existing version 3 brains need no conversion.

Added

  • bf init --no-collect registers a shared brain for search without collection trust, and init accepts a fresh clone of an empty repository.
  • A team brains guide: distinctive names, joining, CI collection with preserved run state, allowlisted memories/ publication, required review of sensor code, and data retention.
  • A documented compatibility policy: the brain, bf.yaml and queries.yaml formats change only in a major release, with manual upgrade steps.

Fixed

  • bf init anchors .gitignore patterns to the brain root, so actions/*/inputs/ stay versioned and a teammate's clone validates like the author's copy. Existing brains that copied the old patterns should prefix inputs/, logs/, memories/, originals/ and .bf/ with /.
  • bf init names a brain after its directory instead of knowledge, avoiding registry name collisions between personal and team brains, and explains how to resolve a taken name.
  • Unsupported platforms fail with a clear message instead of an import traceback.

Changed

  • Call executable collectors sensors in command help, documentation and the bf-maintain skill; record refs, --source, health sources and OKF sources keep their provenance names. search --source now has help text.

v9.0.1 - 2026-09-24

First published Brain Framework release. The v9.0.0 candidate stopped at the macOS ARM64 concurrent-registration check before publication; its tag remains unchanged. This release includes the complete identity and format transition described below.

Fixed

  • Recover when a competing process creates the shared lock during its initial open, while retaining the same confined parent and no-follow checks. A missing or unsafe lock still fails explicitly.
  • Cover initial lock creation races, persistent missing locks and unsafe contenders without relaxing writer serialization.

Breaking changes

  • Rename FKF to Brain Framework: install brain-framework, import bf, and run bf. The repository and documentation move to fmind/brain-framework.
  • Use format 3 for bf.yaml and queries.yaml, with sensors:, {{brain}}, --brain, BF_BRAIN, and a brains: registry under ~/.config/bf/. Private runtime state lives under ~/.local/state/bf/.
  • Organize authored knowledge in projects/, concepts/, and actions/ with ACTION.md; collected records in memories/; collection code in sensors/; maintenance in routines/ and settings/; disposable search in .bf/.
  • Rename workflows to bf-use, bf-learn, bf-maintain, and bf-contribute. CLI and MCP results identify their brain; collection reports identify sensors while record provenance retains source:id and OKF sources.
  • Keep one current format without legacy aliases. Preserve original evidence and per-machine collection trust during any one-time transition, and rebuild caches from the migrated files.

Preserved

  • Offline search and exact reads, two read-only MCP tools, plain-file evidence, bounded trusted collection, atomic recovery and external scheduling.
  • Stable record IDs, provider provenance, explicit concept types, and searchable Markdown in action inputs and outputs.

v9.0.0 - 2026-09-24

Unpublished transition candidate. Publication stopped at the macOS ARM64 concurrent-registration gate; the tag remains unchanged. The corrected transition is released in v9.0.1.

v8.2.2 - 2026-09-24

Fixed

  • Refuse collection into a source with duplicate stored IDs before changing evidence, including snapshot replacement.
  • Serialize concurrent base registrations and write the registry durably with owner-only permissions.
  • Preserve # and percent escapes in note filenames and local links; require exact record IDs in retrieval cases.

Changed

  • Explain the value of shared, file-based knowledge through concrete questions and the search, read, work and update loop.
  • Add runnable team-pilot retrieval cases, routine upgrade instructions and host connection checks.
  • Align documentation and skills on base selection, skill discovery, collection trust, recovery and native scheduling.

v8.2.1 - 2026-09-23

First published 8.2 release. The v8.2.0 candidate stopped at the Intel macOS test gate before publication; its tag remains unchanged.

FKF 8.2 makes incomplete results visible, preserves evidence through interrupted writes, and helps distinguish recently edited knowledge from historical records. Existing version 2 bases need no conversion; disposable search caches rebuild automatically.

Added

  • search --changed-since and --current in the CLI, MCP and retrieval cases, with active, disabled and historical source coverage, freshness and change counts.
  • Reserved record attributes updated, observed and partial distinguish upstream revision, collection time and incomplete content from event time.
  • Scoped local document extraction and optional Calendar agenda snapshots in the standalone collector examples.

Fixed

  • Select test timezones before Python starts, preserving all DST and shared-cache checks on builds without time.tzset(), including Intel macOS.
  • Recover interrupted multi-partition commits from durable originals through explicit build, collection or backup. Serialize source runs and state updates, preserve directory entries before journal writes, and bound partitions before writing.
  • Preserve newer upstream revisions during backfills, repair duplicates and corrupt caches, and retry concurrent cache replacement.
  • Report skipped files and unavailable bases in search; reject incomplete retrieval-case answers. Fail stale-cache health checks and skipped-file updates, reject malformed partition paths, confine link validation, and distinguish unreadable record sources from missing records.
  • Correct recent and identity ordering, per-item passage selection, local-date and DST handling, and malformed-note isolation. Expose identity ambiguity and OKF provenance links.
  • Validate Drive snapshot response identity before accepting empty catalogs, bound provider output while subprocesses run, and cancel collector descendants on SIGTERM.

Changed

  • Rewrite onboarding around a runnable first decision and a small team pilot. Clarify offline and security limits, scheduler cadence and example isolation.
  • Expand failure tests, French retrieval cases and the benchmark's changed and unchanged record paths. No runtime dependencies or services added.

v8.1.0 - 2026-09-23

Added

  • fkf status lists active or blocked project notes whose updated date is more than 14 days old under review, as a reminder rather than a failure.
  • search and read keep local usage counts in the private state directory (time, operation and result count, never the query), summarized by fkf status over 7 and 30 days so an owner can see whether agents use a base. Retrieval cases are not counted.

Changed

  • The example Git collector skips hidden repositories, repositories named with --skip, and bot or reserved-test-domain authors.

v8.0.1 - 2026-09-23

Changed

  • Match English word forms with the FTS5 Porter stemmer, so meetings finds meeting and decided finds decide. Existing caches rebuild on the next search.

v8.0.0 - 2026-09-22

FKF 8 focuses on the loop that makes a knowledge base useful: collect on a schedule, search from anywhere, read the exact source, keep notes current. It removes machinery that made bases hard to run and notes hard to read.

Breaking changes

  • Store records as monthly JSON Lines, records/<source>/<YYYY-MM>.jsonl, with one line per source item upserted by id; snapshot sources keep one snapshot.jsonl. Immutable per-run capture files, capture hashes, latest/historical snapshots and --history are removed; Git or backups keep history. This also removes the 20,000-file ceiling that hourly collection reached within two months.
  • Replace find and context with one search [QUERY] [--since] [--until] [--source] [--type] [--status] [--limit] [--recent]. An empty query lists a time window newest first. Byte budgets are removed; --limit bounds results.
  • Use readable refs everywhere: path, path#section and source:id. Base ids, fkf:// qualified references and record:<hash> URIs are removed.
  • fkf.yaml is version: 2 with a name and no id; fkf.local.yaml is removed. Bases are registered per user in ~/.config/fkf/config.yaml, which also grants collection trust per machine.
  • collect SOURCE [--since] [--until] [--dry-run] replaces positional windows and --preview. build always rebuilds from scratch; --check and --if-stale are removed.
  • Remove containers, parents, --within and indexes/structures.json; link folders and labels with ordinary links. Remove note supersession, the special ## History heading, trust-signal computation, reviewed and effective; note status is one of draft, active, paused, blocked, done, stable, deprecated or archived, and updated dates a note.
  • MCP exposes search and read.
  • Convert a v7 base with a one-off script in that base; see the upgrade notes in the documentation.

Added

  • Search every registered base from any directory, or only the enclosing base; results name their base.
  • fkf register PATH [--collect] adds a cloned team base; a base never runs collectors on a machine that has not trusted it.
  • The search cache refreshes itself incrementally before each query, skips and reports unparsable files, and keeps answering, marked stale, while another writer holds the base.
  • Relative times: now, today, yesterday, 12h, 7d, 2w and local YYYY-MM-DD dates.
  • fkf status [--check] reports each source's last run, success, error and private stderr log, and fails when a trusted scheduled source is stale.
  • fkf update covers every trusted base, resumes each window source from its last success with overlap, catches up at most 30 days, and keeps run state outside the base.
  • fkf validate reports every problem at once, including broken links, missing headings, cited records that do not exist and misplaced or duplicate records.

Changed

  • Rank exact identities first, then items matching all words, then any word; notes above records; deprecated and archived notes last; one result per note through its best section.
  • Collectors run from the base root with the user's environment minus loader-injection variables, and write stderr to a bounded private log.
  • Retrieval cases use version: 2 with expect, forbid, text, empty and time filters.

v7.0.1 - 2026-09-21

First published v7 release. The v7.0.0 candidate stopped at the macOS package gate before publication; its tag remains unchanged.

Fixed

  • Resolve temporary smoke environments to physical paths, including macOS /var, so strict private-state symlink protection remains enabled during distribution checks. Copy packages explicitly when temporary environments and the cache use different filesystems.

Breaking changes

  • Replace the Go implementation with one typed Python package and the fkf command. Python 3.14 or newer on Linux or macOS is required; install with uv tool install --python 3.14 'fkf==7.0.1'.
  • Use one current base format: fkf.yaml, authored projects/, wiki/ and tasks/, immutable normalized records/, and disposable .fkf/ and indexes/. Preserve a v6 base and executable separately; there is no in-place migration or compatibility command.
  • Remove bundled provider integrations, presets, harness installation, execution approval registries and learning proposal machinery. Bases own collectors, schedules and knowledge editing. Reusable Markdown skills and three source examples are maintained separately from the Python distribution.
  • Require an explicitly built, ready cache for indexed retrieval. Missing, stale or corrupt caches name the fkf build recovery command; direct authored and capture-file reads remain available.

Added

  • Explicit update with a side-effect-free dry-run, per-source refresh policy and durable successful automatic checkpoints. Failed windows stay due, manual collections do not move automatic progress, and build --if-stale skips a ready index.
  • Base-qualified exact evidence references, explicit source structure and membership filters, current versus historical capture retrieval, and authored section references.
  • OKF v0.2 wiki structure validation, provenance and asserted verification signals, lifecycle filters, and resumable task folders with inputs and outputs.
  • A runnable fictional base with a credential-free collector and retrieval acceptance cases; focused fkf-use, fkf-learn, fkf-maintain and repository-local fkf-contribute skills.

Changed

  • Keep CLI and the three read-only MCP tools on shared services. Context includes the complete JSON and final newline within four UTF-8 bytes per budget unit; MCP also counts its response wrapper.
  • Match literal lexical terms with case and diacritic folding, explicit identities and authored-note preference. Keep event-time filtering separate from capture recency and decision validity.
  • Confine filesystem access, bound subprocess output and runtime, sanitize collection environments, and preserve atomic immutable evidence and a tested rebuild/recovery path.
  • Publish a documented installation and upgrade path, complete command reference, skill setup example, and contributor release checklist. Remove obsolete implementation proposals from the current tree.

v6.0.2 - 2026-09-09

Fixed

  • Share a fifteen-second deadline across passive-hook children while allowing individual context calls up to ten seconds. Expired budgets prevent new children, and timed-out process groups are terminated.
  • Create every missing parent directory with owner-only permissions during atomic writes, without changing existing directory modes.
  • Isolate the security-rule checkout from Git environment variables inherited by linked-worktree hooks, while retaining rejection of local rule edits.

v6.0.1 - 2026-09-09

Fixed

  • Keep passive hooks compatible with system Python 3.9 and newer, independently of the FKF package’s Python 3.14 environment. The formatter now preserves that syntax boundary and a regression test checks it.
  • Consolidate the hook’s validated direct-argv dispatch through the fixed system env executable.

v6.0.0 - 2026-09-09

Breaking changes

  • Consolidate source execution and improve offline retrieval. Base-owned helpers now live in sources/, retrieval evaluations in checks/queries.yaml, and optional app scripts in clients/. Update declarations and reinstall managed harness hooks, review the resulting execution plan, and renew trust before collecting. Stored evidence remains readable without re-collection.

Changed

  • Select a persistent launcher explicitly with harness print/install --executable when package-manager PATH entries disagree.
  • Reuse status narrative pages for briefing commitments instead of reading and parsing task/project files again.
  • Rename base collection helpers from bin/ to sources/ and retrieval acceptance to checks/queries.yaml.
  • Declare single-script uv app clients under clients:; hash and disclose their separate execution tree.
  • Make CLI context receipt persistence opt-in with --save-receipt, keeping ordinary context reads lock-free.
  • Surface explicit project next actions, review dates, deadlines, and blockers in the offline brief.
  • Improve multi-term lexical ranking and excerpts, reuse Unicode-preserving text analysis, and reduce exact-budget packing work.
  • Let retrieval evaluations require answer-bearing excerpts and verified offline reads, beyond URI recall.
  • Describe harvested lessons as trace citations rather than a knowledge-quality measure.

Fixed

  • Report exact missing completed dates per enabled event source using the configured collection window, including in briefing attention.
  • Reuse task pages for fallback selection and the global learned backlog, and skip a second Markdown parse when rendered headings contain no Learned section.
  • Keep conjunctions out of question scoring and select commitment and body excerpts independently so metadata cannot displace the answer.
  • Give body-cache manifests an independent 8 MiB bound, retaining the 4,096-entry and 512 MiB content limits.
  • Select prompt transcripts through bounded archive metadata and resolve bodies from stored lineage, generation, and turn provenance without changing evidence IDs or deleting history.
  • Search project commitments and preserve active handoffs in compact identity context; unify indexed and fallback lesson backlog semantics.
  • Compile canonical fragment validation once per process and report hook timeouts without exposing child output.
  • Compare GitHub commit bounds as instants, project safe fallback titles for untitled browser visits, and discard RSS stylesheet metadata without fetching it.

  • Open Chromium-family browser roots and profile directories through retained no-follow descriptors so linked path components cannot redirect local history or bookmark collection.

v5.0.1 - 2026-09-07

Fixed

  • Prevent long-lived MCP stdio servers on Python 3.14 from accumulating timeout callbacks during cancellation polling, eliminating age-correlated CPU and memory growth.
  • Let local release verification ignore only uv's exact one-byte dist/.gitignore marker while continuing to reject every other unexpected asset.
  • Parse RSS, Atom, and OPML with a DTD-rejecting XML parser so valid CDATA and predefined or numeric references retain their text without enabling entity expansion.
  • Preserve provider-formatted Gmail recipient names while continuing to derive normalized participant identities from mailbox addresses.
  • Keep agent session hooks non-blocking when invoked with terminal standard input.

v5.0.0 - 2026-09-07

Highlights

  • Reimplement FKF as one typed Python 3.14 package while preserving the fkf command, fkf: 1 configuration and evidence envelopes, trust digests, rebuildable graph and lexical cache contracts, ranking version 7, offline reads, and bounded read-only MCP surface.
  • Publish a wheel and source distribution for uv tool install fkf and one-shot uvx fkf use, with locked uv development, strict Ruff and ty checks, hermetic branch-coverage tests, PyPI trusted publishing, and GitHub build-provenance attestations.
  • Consolidate provider execution behind one direct-argv boundary, package presets and skills as runtime resources, and retain deterministic differential coverage against the final Go implementation.
  • Load the MCP SDK only for MCP commands so ordinary CLI startup does not pay for its server and transport stack.
  • Replace the Hugo module with a locked, self-contained Zensical documentation build while preserving the published Pages routes.

Breaking changes

  • Replace native release archives, install.sh, and the self-replacing fkf upgrade command with standard Python packaging. Use uv tool upgrade fkf for a persistent uv installation.
  • Narrow the existing ?jq= and --where selector spelling to the safe field-path grammar plus optional terminal | length; arbitrary jq programs are rejected instead of running an embedded evaluator.

Upgrade notes

  • Existing bases and collected evidence need no migration or re-collection. After installing v5, refresh official helpers, review and renew execution trust, then rebuild derived caches: fkf config helpers --refresh, fkf trust --all, and fkf build all.
  • Use a persistent uv tool install fkf launcher for harness and schedule integrations. Reserve uvx for one-shot commands.

v4.0.1 - 2026-09-04

Fixed

  • Make Ruff linting independent of contributor-level configuration and use the correct exception types in the Gmail body helper, restoring the clean four-platform release gate.
  • Keep historical agent-session collection available as the append-only store grows by bounding in-window identities before selecting their newest complete generation, while rejecting a partial manifest scan.

v4.0.0 - 2026-09-04

Highlights

  • Make every derived-cache check read-only with build [graph|index|wiki|all] --check, add selective body-cache pruning by source and age, and report lexical-index integrity alongside graph health.
  • Add reviewed Gmail and Calendar body helpers plus four disabled Google Workspace metadata presets, while keeping provider diagnostics private and bounding provider output before it can fill memory or temporary storage.
  • Keep append-only agent-session stores collectible without a lifetime generation ceiling, normalize Git commit titles without discarding raw messages, and refresh the compact embedded usage skill.
  • Harden installation, upgrade, and release delivery with exact dirty-build version handling, portable macOS installation, a four-platform CI gate, draft-before-attestation publication, and exact-head Pages deployment.
  • Rewrite the README around the concrete benefit—owned, inspectable memory shared across coding agents—and tighten the command, source, graph, privacy, and contributor guides.

Breaking changes

  • Remove the documented but ineffective fkf status --all flag; bare status already performs the complete offline check.
  • Emit one {wiki, projects, records, lint?, ok} document from bare structured fkf validate instead of concatenating several top-level JSON reports.
  • Bound receipt.consulted_bodies under the requested pack budget and expose the complete count as consulted_bodies_total.
  • Aggregate text find --count output across the selected window, bound text who neighbours per relation kind, and make body-prune text output explicit.
  • Treat a corrupt lexical cache as a status error. A missing or stale rebuildable cache remains a warning.
  • Remove the unused exported services.StatusRequest.All field and add build/prune request types plus cache-health and receipt fields. Go source consumers using unkeyed literals or strict JSON decoders must update.

Fixed

  • Preserve body-cache crash consistency and confinement across selective pruning, corrupt or missing cache entries, malformed timestamps, symlinked roots, source/age no-ops, and newest-event restore markers.
  • Keep command output inside exact byte budgets, make graph-generation state a published confined URI, and prevent stale or corrupt derived caches from masquerading as current.
  • Refuse unsafe response-file-style body arguments, disclose the real body execution policy during trust review, and retain one finite shutdown path for oversized or uncooperative body providers.
  • Prevent an equal or newer Git-describe development build from being replaced by an older release, and ensure all release archives carry the README, license, notices, checksums, and build-provenance attestations.

Upgrade notes

  • Existing fkf: 1 configuration and evidence remain valid; no re-collection or data migration is required.
  • Refresh any installed official helpers, review and renew execution trust, then rebuild derived caches: fkf config helpers --refresh, fkf trust --all, and fkf build all.
  • Update consumers of bare structured validate and the removed status --all flag before upgrading automation.

v3.0.2 - 2026-09-03

Fixed

  • Keep the cross-process writer-lock test helper alive without triggering Go's deadlock detector, removing a timing-dependent macOS CI failure without changing runtime behavior.

v3.0.1 - 2026-09-03

Fixed

  • Make schedule CLI tests select the native fake scheduler and managed-file layout, restoring the hermetic CI contract on macOS without changing runtime behavior.

v3.0.0 - 2026-09-03

Highlights

  • Add deterministic brief, day, timeline, who, and eval workflows, temporal query grammar, declared identity aliases, and compact text and structured retrieval receipts.
  • Add digest-bound lexical and constant-time graph caches while keeping durable evidence authoritative, offline reads reproducible, and indexed and fallback retrieval semantically identical.
  • Add login-aware opportunistic sync, hourly systemd and launchd scheduling, and idempotent harness integration for Claude Code, Codex, Gemini CLI, Copilot CLI, Antigravity, OpenCode, Grok, Cursor, Kiro, and Cline.
  • Add bounded, ignored, manifest-verified body caching with per-source none, cache, and sync policies; first-class meeting-note and local agent-memory sources can prefetch searchable text without copying it into durable evidence.
  • Expand and harden the reviewed personal presets, session traces, staged learning workflow, MCP surface, provider pagination, process isolation, trust revalidation, and graph generation consistency.

Breaking changes

  • Every collected record must now project one meaningful, control-free title; update custom source schemas and field mappings before the next sync.
  • Structured find and context results omit raw provider records and internal day selections by default; pass --raw only when those diagnostic fields are required.

Upgrade notes

  • Existing evidence remains valid and requires no re-collection. Run fkf build all --base <base> to create the new derived graph and lexical caches.
  • Refresh FKF-owned helpers and harness integrations, review the resulting execution plan, and renew trust before running changed collectors: fkf config helpers --refresh, fkf harness install --all, then fkf trust --all.
  • Body caching stays opt-in per source. The default bodies: none fetches only on an explicit read --body; cache retains an explicitly fetched body and sync prefetches it after evidence is written.

v2.1.0 - 2026-08-30

Highlights

  • Add a dedicated base tests/ execution tree for source verification hooks, recursively covered by trust and prepended to PATH only for fkf test; collection and body commands cannot see test fixtures or shadows.
  • Report source-hook readiness separately from ordinary requires:, disclose bin/ and tests/ as distinct trust items, and carry the new layout through init, permissions, schemas, documentation, and bundled skills.
  • Preserve v2 compatibility: bases without tests/ keep their existing trust digest, hooks can still resolve from bin/, and an empty optional selection remains a successful 0/0 report. Completion gates should name mandatory sources.

Fixed

  • Restrict repository metadata projected by bundled session, Git, and agent-hook helpers to GitHub remotes, while continuing to strip credentials and reject malformed paths.
  • Open Atuin history read-only in batch mode, omit deleted rows and command text, and declare the Git dependency used by the agent-sessions preset.

Upgrade notes

  • A pre-existing base tests/ directory is now reserved, recursively trust-covered execution material and must contain no symlinks. Move source hooks and their support files there, keep generic repository tests elsewhere, then review and renew trust.

v2.0.1 - 2026-08-29

Fixed

  • Make verified release archives the documented v2 installation path and explain that Go's major-version import rules keep the unchanged module path's go install ...@latest resolution on v1.

v2.0.0 - 2026-08-29

Highlights

  • Add optional, trust-covered source test: argv and fkf test, with enabled-source defaults, explicit disabled-source selection, bounded timeouts, stable reports, and provider-stderr privacy.
  • Publish graph.meta.json schema version 2 with separate SHA-256 inputs for events, index, projects, tasks, wiki, and edge-relevant schema semantics, plus a framed aggregate and exact graph.tsv output digest.
  • Give every bundled shell helper an explicit .sh extension and require .sh or .py when scaffolding a helper, including the interpreter in the generated readiness contract when needed.

Breaking changes

  • Existing derived graph metadata must be rebuilt with fkf build graph; collected evidence remains readable and requires no re-collection.
  • Base configurations and harness integrations using bundled extensionless helper names must move to the corresponding .sh names before refreshing helpers.

v1.1.2 - 2026-08-27

Highlights

  • Flatten the documentation sidebar on desktop and mobile so Overview and every guide are peers, while preserving all published routes and enforcing the rendered navigation contract in tests.

v1.1.1 - 2026-08-27

Highlights

  • Make failed collection diagnostics actionable with the source, date or window, safe substituted command, neutral working directory, timeout, and exit class while keeping provider stderr and body-derived arguments private.
  • Stage release installation beside the destination before an atomic replacement, preserving an existing binary if staging fails; cover every published Linux and macOS architecture tuple hermetically.
  • Add a dedicated configuration-schema guide, present every documented agent harness at the same level, refresh vendor hook contracts, simplify root help and contributor instructions, and keep the Overview first in the documentation tree.
  • Scope toolchain drift checks to project pins, refresh the embedded usage skill and supported-version policy, and retain a strict, generated, link-checked documentation contract.

v1.1.0 - 2026-08-27

Highlights

  • Add fkf upgrade, which selects the current platform archive, verifies its published SHA-256 checksum and reported version, and atomically replaces the running executable.
  • Send the documentation root directly to the Overview, expose Overview in the navigation, and remove the intermediate "Read the docs" landing page.
  • Explain repeated fkf sync safety and how coding agents learn from project and wiki content through the read-only MCP server and embedded skills.

v1.0.0 - 2026-08-26

The initial Fmind Knowledge Framework release: one Go binary that collects developer activity into an owned, inspectable base of JSON and Markdown, links it as a graph of relative URIs, and gives coding agents a deterministic context pack under a token budget.

Highlights

  • Collect complete daily events and point-in-time indexes from local tools, GitHub, Google Workspace, and Google Cloud presets, plus arbitrary reviewed provider commands.
  • Compose each source from direct argv or a trust-digested helper whose shebang selects its interpreter, with curated preset helpers for provider boundaries that need shared pagination, completeness, or privacy handling.
  • Declare one root semantic schema: with descriptions, cardinality, examples, and relation roles; sources associate those shared fields with provider paths, and stored documents retain the exact schema subset they used.
  • Build an open, transcription-only graph: any non-reserved lowercase entity scheme is valid, relation field names are base-defined, and edges come only from declared fields, authored links, tags, and explicit relations: frontmatter.
  • Read, find, rank, and graph stored knowledge entirely offline; the read-only MCP server cannot collect, write, shell, or fetch bodies.
  • Share one base across Claude Code, Codex, Gemini CLI, OpenCode, Copilot CLI, Antigravity, Cursor, Kiro, Cline, and other harnesses through portable Agent Skills and documented hooks.
  • Keep execution explicit with a canonical-plan trust digest covering enabled commands, body-bound paths, helper scripts, executable bits, executable search directories, retry, pacing, and collection policy without re-arming on YAML presentation, inherited environment, or retrieval-only metadata.
  • Use one explicit fkf: 1 marker value for strict base configuration and the separate additive evidence envelope.
  • Store plain JSON and Markdown in five typed layers, with rebuildable graph.tsv and graph.meta.json at the base root, strict schemas, bounded and atomic I/O, path confinement, owner-only permissions, and untrusted-content framing.
  • Reproduce lexical retrieval under a hard whole-pack budget with a selection receipt that records scores, reasons, counted exclusions, rejected pins, evaluation day, ranking version, and semantic-input digest.
  • Ship synthetic demos, personal and team presets, a strict Hugo documentation site, hermetic race-tested Go suites, security scans, reproducible archives, checksums, and build-provenance attestations.

Supported platforms

Release archives are provided for Linux and macOS on amd64 and arm64. Native Windows is intentionally out of scope; WSL2 uses the Linux archive.