The words, in plain terms #
One sentence each. The rest of the page explains how they work.
| Word | What it means |
|---|---|
| brief | The short handover your agent gets before its first turn: the rules, how you like to work, pinned facts and the state of the code, capped at about 1.5k tokens. |
| recall | A search of the memories with the best match first. You can run it, the agent can run it and a hook runs it on every message. |
| scope | A label such as deploy:prod that says where a memory applies. A narrow scope sees the wider ones above it, never the other way round. |
| tier | The shelf life a memory gets from its type. It decides how fast newer facts start to rank above it. |
| supersede | To save a correction that replaces an older memory, so a question in the old words gets the new answer. |
| guardrail | A rule saved as a policy or constraint. It goes into every brief, pinned or not. |
| axiom | A rule that holds in every project, not just this one, shown at the top of each brief. |
| hook | A command your agent runs by itself at a fixed moment, such as when a session opens. The brain reaches the model without the model having to ask. |
| MCP | The standard way an agent calls outside tools. Through it the model can search the brain and write to it when it decides to. |
What a memory holds #
A memory is a title, a body and a set of fields that decide how it ages and where it ranks. Each field is a real field with fixed values, not free text.
| Field | What it decides |
|---|---|
| title | Together with scope, it says which memory this is. Case and spacing are evened out, so saving the same fact again updates it in place instead of adding a second copy. |
| body | The fact. Write it for a stranger reading it six months from now, not for the session saving it. |
| type | How fast it fades, whether a commit can make it wrong and whether it gets a review date. |
| scope | Where it applies. Levels are split by : and a memory is seen from its own level and the ones below it. |
| confidence | confirmed, reported or assumed. Helps break ties when ranking. |
| importance | 1 to 5, with 3 as neutral. How much it matters, not how well it matches. |
| source | Where the fact comes from. You need one to save something as confirmed. |
| trust | stated, imported or external. Where the memory came from, which is a different question from how solid it is. |
| source_commit | Added automatically on the types a commit can make wrong. It is how recall tells you whether the code has moved. |
| valid_from / valid_to | When the fact became true and when it stops being true, for facts with known dates. |
| superseded_by | The correction that replaced it. Recall follows this link to the newer fact. |
| pin | Always in the brief. |
| session | Which session saved it. This matters once more than one agent works on a project. |
Types and how fast they fade #
The type you pick sets the shelf life. It is the choice that matters most when you save a memory, which is why in practice you always pass --type.
| Tier | Half-life | Weight | Types |
|---|---|---|---|
| A | never decays | 1.30 | infra access gotcha incident |
| B | never decays | 1.20 | decision constraint commitment |
| C | 60 days | 1.00 | preference relationship |
| D | 4 days | 0.70 | state |
| N | never decays | 1.00, neutral | knowledge brand sop process standard policy report note |
Preferences fade in search results but not in the brief. A preference is tier C, so in a search it slowly gives way to newer facts. The brief's "how they want to work" section ignores fading. It takes up to eight preferences and relationships, picked by importance and by when they were last updated. A rule you stated once, such as "never force-push", keeps showing up at the start of every session until you replace it or more important ones push it out of those eight. Pin it if it must always be there.
Fading never deletes. A tier D memory a month past its half-life is still there and an exact search still finds it. It just no longer ranks above things that still matter. That is what you want from "the box is rebooting" and nobody has to remember to clean up.
incident is tier A, not tier D
An outage feels short-lived but the record of it is not. What happened to a system stays true. An incident record is worth most to whoever asks about it months later, not that week.
Rules skip ranking
policy and constraint memories go into every brief, pinned or not. A rule that could lose its place to something more topical would not really be a rule. The brief shows up to twenty, most important first. Any beyond that are counted on a line of their own, never dropped without a word.
How results are ordered #
Recall works in two steps. First it finds the memories that match your words. Then it puts them in order.
Finding the candidates
The search is full-text, like a search engine over the brain. The brain uses SQLite's built-in full-text index. Any word of your question can find a memory. A memory then stays only if it holds most of the question's words: both of them for a one- or two-word query, about three in five for a longer one. When nothing gets that far, which is normal for a full sentence, recall keeps the weaker matches and tells you so ("loose": true in --json).
Ordering them
score = relevance × coverage × recency × stakes × scope relevance how strongly the words matched, mapped onto 0.35..1 so a matched row can never score as if it had not matched coverage the share of your question the memory holds: all of it scores 1, half of it 0.5 recency 0.5 ^ (days / tier half-life), or 1.0 for the tiers that never decay stakes tier × (importance / 3) × usage × confidence, clamped to the band 0.70 .. 1.43 scope when you ask in a scope: 1.6 for that scope, 0.25 less per step out to its parents, never below 0.8
A pinned memory doubles its score only when you browse with nothing to match. When you ask a specific question, a pin does not push it up. It is in every brief anyway.
Why the stakes term has a cap
It had none at first. We measured it on this project's own 571 memories over ten real queries. How well the words matched varied by 1.12x while the multipliers varied by 43.75x, so in practice the multipliers decided everything. 54% of result pairs came back out of word-match order, with memories jumping up to twelve places. In every worst case the memory pushed down was the one that actually answered the question.
The rule now: what kind of memory it is breaks ties. It does not beat what matched. The cap is not a guess. It is the widest gap between tier weights (1 / 0.70), so types still rank in their usual order and only several signals stacking up is held back.
Fading sits outside the cap on purpose. It says whether something is still true, not how much it matters, so a four-day memory that is thirty days old can sink out of sight.
The usage term
A memory that keeps coming back in results and being used gets a small boost with a cap: 1 + min(0.15 × ln(1+uses), 0.5). Every recall counts as a use unless you pass --no-touch. That flag is there so cleanup runs and scripts do not pump up the very numbers they are checking.
Where a memory applies #
A scope is a path with levels split by :. A search sees its own level and every level above it, never the levels beside or below.
$ ns-brain recall "cert renewal" --scope channel:deploy:prod sees: channel:deploy:prod channel:deploy channel global never sees: channel:deploy:staging, or anything below it
Everything defaults to global, which means "everywhere in this project", not "everywhere in the world". You declare scopes in brain.json with ns-brain scope. ns-brain map compares what is declared with what is actually stored, so a scope someone made up by a typo shows up as one nobody declared.
Use scopes when a project has truly separate areas. Two or three are worth it. A dozen is a filing system you will stop keeping up and a memory left in global is still found.
How sure and from where #
Confidence says how sure the fact is. Trust says where it came from. The brain keeps both.
| Confidence | Weight | Means |
|---|---|---|
| confirmed | 1.15 | It has a source somebody else could check: a path, a command, a URL, a commit or the user saying so. |
| reported | 1.00 | The default. Believed, not proven. |
| assumed | 0.80 | Worked out, not checked. Kept because losing it would be worse and ranked lower for it. |
high, medium and low also work and mean the same three. Some other tools use those words and translating them is better than quietly saving a value nothing understands.
Your agent cannot just call something confirmed. It needs a source in --source or --ref. A standing preference has no source but the person who said it, so --source "stated by the user" counts. The brain refuses a confirmed with no source and the message names the flags that fix it. It used to save it quietly as reported instead, but the warning went where hooks and MCP results never show it. Sessions thought their saves were confirmed when they were not.
Trust is a separate field. stated is the default and means it came from this project's own work. imported is set on everything that comes in through import. external marks a claim from outside. An imported dump is outside input like any other and the label stays on it so a later session can tell.
Corrections #
A markdown file has no way to say that one statement replaced another. The brain does.
$ ns-brain remember --type infra --supersedes 31 \
--title "Postgres, not SQLite: three writers" \
--body "moved to Postgres 2026-03-04; the WAL contention..."
The new memory takes over the old one's identity. The old one stays as history with a superseded_by link and recall follows that link: a search in the old words gets the current fact, not both at equal weight. --all shows the replaced versions when you want the history.
Two guards protect this:
forgetrefuses to delete a memory that replaced others. Deleting it would break those links and bring back facts that were withdrawn on purpose.--supersedeson a memory that another correction already replaced needs--fork-ok. Two current versions of one fact is exactly what corrections are meant to stop.
Saving the same thing twice #
A memory is identified by its scope and its title, with case and spacing evened out. Saving the same title again updates that memory in place. If the body is different, the brain refuses until you pass --update (the old body is kept in history) or --supersedes, so a careless save cannot overwrite a fact. The harder case is a second memory about the same subject under a different title. The brain refuses that too: if half or more of the words in two titles overlap, the save is blocked and you get the existing id and three ways forward.
#42 in channel:deploy is already about this (73% title overlap): stored: prod deploys run from the release branch only via ci yours: prod deploys run from the release branch through ci Storing both would put two answers to one question in the brain. Pick one: the fact CHANGED --supersedes 42 same fact, better words ns-brain update 42 --body ... genuinely separate --allow-duplicate
ns-brain review also reports duplicates, but only after the second copy exists and usually to a different session than the one that made it. The moment of saving is the only time the agent still has the text in hand.
Titles under five words are not checked. prod db host and prod db port overlap 0.50 and are different facts. On this project's own titles (7 to 47 words, median 15) that limit costs nothing. When the check is unsure it lets the save through, which is the right call for a rule of thumb: you just get the old behaviour.
Is this fact still current? #
A memory about the code records the commit it was true at. When recall returns it, you get one of these lines:
tree unchanged since stored (a41f22c9) 14 commits since stored (a41f22c9) — re-check against source stored at a41f22c9, which is not in this tree — re-check against source
So you still read the files sometimes, but only for the facts that need it. The brain tells you which ones are safe to act on without opening anything.
Only gotcha, state and incident get the commit stamp. The list is short on purpose: a warning that shows on every memory is a warning everyone learns to skip. A decision never gets one. Only the person who made a decision can make it wrong, never a commit. Flagging one as stale just invites people to argue it again.
A memory from another project in a shared store gets no line either. It was recorded against a different codebase.
Facts that need a recheck #
Some facts can change outside your code. A host moves, an IP is reassigned, a key is rotated and nothing in the project records it. There is no commit to count and nobody to correct it, so the fact looks just as current on the day it goes wrong. We once had a session call a box staging from a 25-day-old note. It had been production for two weeks.
So infra and access memories get a review date 30 days out by default and the save tells you so. Thirty days because a reminder that fires every week is one everybody learns to scroll past.
$ ns-brain remember --type infra --title "..." --body "..." remembered #31 [global/infra] ... review on <30 days from today> (default for infra, 30d — --review <date> to move it, --no-review if it is permanent) $ ns-brain verify 31 --source "checked against the live box" $ ns-brain review --stale-days 90 # tier A/B nobody has re-checked
What your agent gets at the start #
The brief your agent gets when a session opens is not a dump of everything. It is built in order of priority and has a size limit. When it runs over, it cuts the least important parts, names what it cut and prints the command to see the rest. It cannot grow to 73KB, because there is a number stopping it: 6000 bytes by default, about 1.5k tokens.
The priority decides what gets cut, not the order things are printed in. Rules are kept first, then how the user wants to be worked with, then pinned memories, then what is due, then the state of the code read live from git, then the inbox, then recent activity. If something has to go, it goes from the end of that list. The printed order is different. First comes what is from outside the project: axioms, house standards, the inbox and the list of other projects. Then the first of this project's own sections is how the user wants to be worked with, ahead of pinned facts and rules, because it shapes how the agent reads everything below it. Empty sections are left out.
Two hooks deliver it. SessionStart runs ns-brain wakeup before your first turn. UserPromptSubmit runs ns-brain hook-recall, which searches the brain for what you just typed and adds what it finds. Neither waits for the model to decide to look. Anything that depends on someone remembering to check will one day be missed.
What is not a memory #
Three things live in the brain but are kept apart from memories on purpose. Mixing them in would throw off the ranking.
- Measures. A memory tells you what is true. A measure tells you what a number was and which way it is going. Readings are not searched or ranked at all, so a number can never push a fact down.
- Tasks. A memory is true until something changes it. A task is owed until it is done. Tasks show up through the hooks that already run, so a task on a project nobody opens for two weeks waits two weeks.
- Events. The activity log. It only ever grows, it is never ranked and it is how you find out what the last session actually did.
Tasks, measures and the opt-in domains →
What is worth storing #
The real risk is not an empty brain. It is a full one nobody trusts. We saw a tool that learned on its own fill its store with two thirds function renames and generic advice, all filed under the widest scope. The machinery was excellent. What goes in decides whether anyone reads what comes out.
Do not save what the repository already has: how the code is laid out, what a function does, a fix that is in the git history, anything in the project's own instructions file. A session can read those and an out-of-date copy is worse than none.
Do save the reasoning nobody wrote down: why the obvious approach was turned down, what broke last time and what it looked like, the limit that is not in any file, the preference the user stated once and expects to stick. BEST-PRACTICES.md is the long version and it ships in the binary.