billing project and a web project. Paths are shortened.
Why 0.16.0 is a different base
In 0.15, when a prompt matched a domain’s keyword, base printed that domain’s rules. If the text was longer than Claude Code accepts from a hook, Claude Code cut it at its limit, and nothing told you or Claude what was lost. A rule arrived because a word matched, whether or not it fit the work. A rule that was wrong, or never arrived, stayed that way until you noticed and fixed it by hand. 0.16.0 changes three things. Nothing is dropped without a word. base measures its text before printing it and fits it to a budget in bytes, the size Claude Code was measured to deliver whole. When something does not fit, base leaves out whole rules, least important first, and says what it left out and which command prints it. Rules that do not fit still reach Claude as a short title, with an instruction to read them before acting. The right rules for the work. Rules are ranked by how well they fit the prompt. The file being read or edited decides which project’s rules load. A rule can name its own words, folders, tools and commands, and carry test prompts that prove it fires where it should. Your corrections improve your rules. base notices when you correct Claude and asks Claude to file the correction as a proposal. Nothing changes until you approve it. You can see what base matched for every prompt and why, andbase doctor tells you which rules never fire, fire too often, or keep getting corrected.
Upgrading from 0.15
Getting 0.16.0 can take one command on an older install. After that there is nothing to run, for you or for your Claude. The update backs up every file before it changes it, and every change comes with the command that undoes it.How you get it
If base updates itself on your machine, 0.16.0 arrives on its own. A session start finds the release and installs it in the background, and the next session runs it. Most 0.15 installs do not update themselves. The base installers from 0.7.0 to 0.15.2 turned developer mode on for everyone, and base does not update itself while developer mode is on. Those installs show a weekly “base update available” banner instead. Run this once:Your first session
The first session on 0.16.0 opens in the new layout described under Session start. Behind it, base starts a one-time tidy of your base data and settings and does not wait for it, so that start is no slower. Near its end, one line says base rewrote its own section of your~/.claude/CLAUDE.md:
~/.claude/settings.json, after a backup, keeping every other entry. It tells base which sessions ended, so the rule pass described under When you correct Claude knows what to read.
Your second session
The second session tells you what changed. If your~/.base-gbl/base.toml still has the line the 0.15 installer wrote, it opens with this paragraph, and Claude is asked to show it to you word for word before anything else:
base doctor keeps showing the developer-mode paragraph for 14 days, in case you missed it.
What changes, and what stays
What the update can change, and only where it applies:- Developer mode, when the 0.15 installer turned it on. Claude stops ending every reply with a DEVMODE block.
- Your saved notes at session start get up to 4,000 characters instead of 2,000.
- Folder rules are written out with their whole path, so they cover the same place from any folder you start in. A rule on a folder that holds two or more of your projects now loads for files in that folder outside those projects. 0.15 silently ignored such rules.
- Starter commands you never edited (
*base,*handoff,*fork,*end) get this version’s text. - Your base data is tidied. Entries that belong to another of your workspaces move back there, or into a separate file in this workspace when base cannot find that one, so nothing is lost. A correction that names exactly one entry is linked to it. Small mix-ups in how entries point to the ones they replace are fixed, repeated lines are removed, and only the newest 3 graph backups are kept.
- Claude Code’s files: the SessionEnd entry and base’s own CLAUDE.md section, as above.
- Every rule, decision, note, handoff, fork, project, task and reminder you have. An entry may move to the workspace it belongs to, but none is deleted.
- A starter command you edited. If it is your
*baseand still has the oldrule addline, session start says so once. - Developer mode, if you turned it on yourself.
- Everything in
~/.claude/CLAUDE.mdoutside base’s section, and every other entry in~/.claude/settings.json.
Undoing it
Each line above ends with its own undo.base doctor --restore <path> puts back a backup base made, either a graph snapshot or a *.BAK-<date>-pre-<version> copy beside a config file, and refuses any other file, so it cannot overwrite something base did not write.
To turn developer mode back on:
One case needs a person. If you renamed a workspace folder after base wrote data for it, its older entries stay under the old folder name.
base doctor explains what it found and leaves them alone, because only you can say whose data it is.What base adds to each message, and what it holds back
Every prompt you send can carry text from base: rules matched to your prompt or to a file the session touched, that domain’s context, relay messages, your global rules and decisions, and bracket rules, in that order of importance. 0.16.0 builds this text as named blocks and measures it in bytes against a budget,[budget] prompt_bytes, which is 10,000 bytes by default. That is the size measured to arrive whole on Claude Code 2.1.287. Inside a block, rules are ranked by how well they fit the prompt: shared words count, and rare words count more. Keywords and folders still decide which rules are served; the score only decides their order.
Here is the start of what Claude gets for the prompt “the invoice total is a float, fix the button that shows it”. The numbers are each rule’s place in its domain; the order is the ranking, so the rule about floats comes first:
[budget] session_start_bytes. Both write their full, untrimmed text to .base/hook-output/<session id>/, so each session has its own copy.
When your Claude Code changes. The budgets were measured on one version of Claude Code. base doctor compares that version with the one you run and tells you when to measure again:
base doctor --measure asks Claude Code, with a few headless calls on a cheap model, how much of each hook’s text actually reaches the model, then writes the budgets. Nothing runs it for you, because it spends model calls.
More detail: Debugging injection, Context reference, Config and secrets.
Rules follow the file you touch
When Claude reads, edits or writes a file, or a shell command names one, base finds the deepest registered project folder that holds that file and loads that project’s rules. No trigger needed. The rules of other projects stay out. In the example workspace, Claude editsweb/src/button.tsx. Beside that edit, Claude sees the web project’s rule. base matched only the web project, as the log below shows:
billing/src/invoice.rs and base printed nothing at all, because the prompt before it had already delivered the billing rules. base log matches shows both, and why:
--keywords "refund, refunds"(or--words): served when the prompt has one of these words.--place <folder or file>and--path <file or folder>: served for work in that place.--tool <name>and--command "<command>": served when Claude is about to use that tool or run that command, at most once every 10 minutes per session.
cut 31 on the first row above is the 30 global rules held back for space, plus one rule with a word of its own, currency, which was not in the prompt.
A project inside another one can carry its parent’s rules: base project update <slug> --parent <slug> --nested true. Work in the child then loads the parent’s rules too, after its own.
Rules carry test prompts. --fires-on "<prompt>" names a prompt that must serve the rule, and --quiet-on "<prompt>" one that must not. base rule test runs them through the same matching the prompt hook uses:
base rule add refuses a rule without keywords and a --fires-on prompt, so every rule Claude adds arrives with its first test.
Projects know their folder. base project paths --suggest lists every project whose folder is missing or too broad and suggests the right one, and base project rename <old> <new> renames a project and its domain everywhere, keeping the old name working.
More detail: Rules and domains, Domains and triggers, Tiers and scoping, Projects and tasks, Standards.
When you correct Claude
In the example session, Claude got the refund rule on the first prompt and answered wrongly anyway. The next prompt is a correction:no, that’s wrong. refunds never call the card API directlybase reads the wording, and that prompt carries one extra line for Claude:
CORRECTED: line is Claude’s own. base install offers to add one line to ~/.claude/CLAUDE.md asking Claude to start a corrected reply that way. If you decline, session start carries the line instead.
Nothing changes until you say so. Your next session start says:
base rule review walks the proposals with one key each: a approve, e edit, r reject, s skip, q stop. Before you approve a change, base replays it over your recent prompts and shows which would start or stop serving the rule; base rule replay runs the same check by hand. A change that would fire on more than a quarter of them is flagged TOO BROAD and needs --broad-ok. A proposal you reject is never made again.
The rule pass. base tune reads the sessions since its last pass and proposes keyword fixes, new rules, merges, splits and retirements, judged by a cheap model with its answers cached. It never runs inside a hook. When a pass is due, the next prompt says so. base tune --dry-run shows what it would read.
Shadow mode. base shadow start runs a different matcher beside the live one on every prompt and file touch, while only the live one’s choice is printed. base shadow report shows where they differ and whether your corrections favour the candidate.
More detail: Corrections and tuning, the new guide for all of this.
Doctor and the match log: seeing what base did
base log matches shows every prompt and file touch: which domains matched and by what, how many rules were served, and how many matched but were held back or left out, as in the rows above. --json adds each rule’s id, score and reason. The rows live in .base/match-log.jsonl for 90 days. Prompt text is kept with secrets such as API keys, tokens and passwords replaced, and [log] prompt_text keeps less if you want.
base log corrections shows what base read as a correction, in a session or a whole transcript.
base doctor now reports what each hook printed against its budget, and adds a section on how your rules are used, read from the match log. On the example workspace it says:
base doctor --fix prints a plan for repairing what doctor reports and changes nothing; base doctor --fix --yes applies it after a snapshot. It is the same repair the update runs.
base --version now names the commit it was built from, for example base 0.16.0 (build 1a2b3c4d5e6f), so two builds of one version can be told apart.
More detail: Graph health, Debugging injection, Setup reference.
Session start
Session start has a new layout: one header line, a short instruction block for Claude, then what is due, your handoffs, forks, projects, tasks and milestones, and then the rest. From the example workspace:base handoff show also takes a slug, a project name or a few words. Each session reads its own letters, so another session’s start does not change what yours point at.
Your saved notes have their own budget of 4,000 characters, filled with whole notes only, and the block ends with how many were left out. Tasks and milestones from both your workspace and the global tier are listed, and the block says which data it read, so an empty list is never mistaken for missing data.
More detail: Handoffs and forks, Sessions reference, Where things live.
Relay
0.15 pasted the whole relay watcher script into prompts and tool calls. 0.16.0 sends one line instead, and only to a session whose relay title is in use, meaning it sent or received a ping or registered by hand:base relay arm prints exactly what to start. Relay messages read as information, never as orders that outrank you, and each one is shown once.
A ping now reaches the session it was sent to. When a title passes to a new session, the old session’s undelivered pings are archived and each sender is told who holds the title now. A ping that waits more than a day undelivered is archived the same way. The inbox watcher base ships no longer drops pings when more than five are waiting.
More detail: Multiple sessions, Sessions reference.
Reminders, handoffs and deferred work
Reminders can be snoozed, archived and brought back:base reminder snooze <number or slug> <duration>, base reminder archive, base reminder unarchive, and base reminder list --archived. All of them work across your workspace and the global tier.
An overdue reminder warns before it archives itself. From 8 days overdue, its line says when it will go, as on the certificate reminder above. It archives itself only when it is 10 or more days overdue and that warning was first shown at least 2 days earlier, and the next session start says so once with the command that brings it back. It is kept, never deleted.
A new handoff archives only the earlier one in its own lane: the earlier one by the same author, or in the lane you name with --lane. Several sessions on one project no longer archive each other’s handoffs. base handoff unarchive <slug> and base fork unarchive <slug> undo an archive. A handoff, fork, decision or note written from a folder inside ~/.base-gbl now goes to the workspace around that folder, not the global tier, and opening a handoff or fork doc counts as activity on it.
Deferred work is off until you turn it on with [defer] enabled = true. Then handoffs, forks, tasks and milestones nobody touched for 10 days (set per kind in [defer.days]) stop showing at session start and stay listed. base handoff deferred, base fork deferred, base task deferred and base milestone deferred list them, each with the command that brings it back. base reconcile --dry-run shows what a pass would defer before you turn it on.
More detail: Handoffs and forks, Projects and tasks, Star commands.
Sync and the code map
One bad file no longer stops a sync. A file whose frontmatter holds a value the graph cannot store used to end the whole sync before anything was written. Now base skips that file, names it, writes every other file, and exits with code 3:base sync --ast --yes --no-time-limit --target <folder>. The hint to use base ast query now appears only for a search of source code in a mapped app, and never tells you to map an app base maps by itself.
Fixed from reports: [[wikilinks]] in frontmatter lists extract cleanly, sync.exclude holds through links such as symlinks and junctions, a pnpm workspace that lists its own root maps, and a file name with a character like → no longer breaks session start.
More detail: Navigating code, Ingesting documents, Code reference, The graph.
Where to read more
Every page below was updated for 0.16.0.Corrections and tuning
New in 0.16.0: how base notices a correction and turns it into a rule change you approve.
Rules and domains
Matchers of a rule’s own, test prompts, and the file you touch.
Debugging injection
What base sent, what it held back, and why.
Updating base
What the update changes by itself, and how to undo it.