What your agents inherit
A hands-on ENGRAM walkthrough · August 21, 2026
1. Why this tutorial exists
You point a coding agent at ENGRAM and it starts recalling things. Useful immediately — and quietly under-specified, because retrieval is associative. It finds what is related. That is exactly right when you are exploring, and exactly wrong when the question has a correct answer and a plausible-sounding neighbour.
The failure is not that the agent finds nothing. It finds something adjacent, states it confidently, and you have no signal that it was working from the wrong document. Two agents on the same repo end up building against two different readings of the same design, and neither is obviously wrong until the code disagrees.
Ground truth is the fix: a project declares which articles are authoritative, and retrieval performed under that project prefers them. The part worth a whole tutorial is what happens next — the agent needs to know nothing about it. There is no parameter to pass and no instruction to write. The agent declares which project it is working in, which it already does to keep its memory straight, and the brief comes with it.
That is convenient, and it means something is being inherited whether or not you chose it. This tutorial is about seeing that clearly.
Prior tutorials: Tutorial 7 (an agent with a memory of its own), Tutorial 4 (a harness and its team). This one stands alone.
2. What you'll build
By the end you will have:
- read back what a project resolves as its ground truth before changing anything;
- understood the three inputs that produce that answer, one of which is silent;
- declared a brief of your own and watched an agent pick it up without being reconfigured;
- chosen between being informed by a document and bound by one, knowing what each costs.
3. Prerequisites
| Requirement | Notes |
|---|---|
| An ENGRAM account + MCP connection | Claude Code, Claude Desktop, or any MCP client |
PROJECT_AUTHORITY_ENABLED on | off by default; an administrator flips it in Admin → Settings. Off, everything below is inert |
| An article you'd call authoritative | your project must be able to read it — see Part 4 |
| A project | if you have used ENGRAM at all, you have a default one |
4. Part 1 — Read before you write
Start by asking what a project already resolves. Do this first: the answer is frequently not what people expect, and it is much easier to reason about a change from a known baseline.
get_project_authority(project="engram-personal-kb")
project takes a project_id or a repo_url, so you can
name the project the way you happen to have it to hand.
Three shapes of answer, all legitimate:
| What comes back | What it means |
|---|---|
| a list of articles | the project declares its own ground truth |
| an empty set, and the project inherits | the agent is reading ENGRAM's corpus, not yours |
| an empty set, no inheritance | no ground truth — retrieval is ordinary and associative |
The third is not a failure. It is the state every project was in before you declared anything, and it is what most projects are in today.
5. Part 2 — The three inputs, one of which is silent
The brief is a union, resolved at query time:
brief = (what the project declared)
∪ (ENGRAM's default corpus, IF the project inherits)
Each article then resolves to its current chain head, so an article revised after you declared it still counts. Declaring ground truth does not pin a version, and a brief cannot silently lose an article to an edit.
The silent input is inheritance. ENGRAM ships its own documentation as a ground-truth corpus, in one system-owned project, and other projects draw on it by flag rather than each holding a copy. So "how does recall actually work?" asked inside an inheriting project is answered from ENGRAM's own reference instead of from whatever you happened to save. Publishing a corpus article is one write, and a new user inherits at creation.
inherit_engram_defaults means "does not inherit". Not "inherits".
Projects created before the flag existed keep exactly the brief they had. That is deliberate and worth stating plainly: a deploy never widens a brief. If a project had been scoped down to three documents on purpose, an upgrade that quietly unioned in five more would be a correctness bug wearing a feature's clothes. New projects get inheritance at creation; older ones were switched on explicitly, by a migration someone had to run and check.
So when a project resolves an empty brief and you expected one, the cause is almost always one of three things, in this order:
PROJECT_AUTHORITY_ENABLEDis off;- the project declared nothing and inherits nothing;
- the project predates the flag — so inheritance is absent, i.e. false.
6. Part 3 — Declare a brief
set_project_authority(
project="engram-personal-kb",
article_ids=["article:...", "article:..."],
policy="prioritize"
)
Four properties worth knowing before you run it.
It replaces, it does not append. Each call defines the complete set, so re-declaring the
same ids changes nothing and article_ids=[] clears the declaration. Idempotent by construction
— safe to put in a setup script that runs more than once.
The articles must be readable by the project, and a private one is refused loudly. The rule is readability, not a particular visibility value. A worker resolves a brief's entities through the graph, so a private article declared as ground truth would be invisible to the very team it exists to anchor — a success response and no effect. A 400 you have to act on beats a silent no-op you discover months later.
So the guard asks one question — can this project's members read this article? — and the three visibility values answer it differently:
public— always. Readable by everyone.project— only if you own the article. Declaring auto-adds it to the project's collection, and that is what makes it readable by the members; only an artifact's owner may place a non-public artifact into a project, or declaring would become a way to grant yourself a read of someone else's material.private— never. No membership widens a private artifact, so the team still could not read it.
That is the change worth knowing: a team's brief no longer has to be published to the world to be
shared with the team. Widen it with set_article_visibility — to
project for your project's members, or public for everyone.
Revisions are followed, per Part 2. get_project_authority flags
is_latest=false if an article has been revised since you declared it, so you can see the chain
moved.
Ambiguity fails closed. If two projects claim the same repo_url, resolution
returns neither rather than picking one. A wrong project does not throw — it silently
anchors an agent to another team's ground truth, which is worse than no anchor at all. If a brief you just
declared does not appear, check that exactly one project holds that repo.
7. Part 4 — Informed by, or bound by
One setting, the largest behavioural difference in the feature.
prioritize (default) | restrict | |
|---|---|---|
| Brief articles | ranked up | the only articles returned |
| Other visible articles | still returned, ranked normally | excluded |
| Documents, memories, code sessions | unaffected | unaffected |
| Use it for | a report that must be informed by ground truth | a team that must be bound by a contract |
Two things people get wrong here.
restrict filters the article arm only. It never restricts documents, memories,
or code sessions. It is not a general-purpose blast shield, and reaching for it to make an agent "only use
approved sources" will not do that.
Under restrict, "ambient" includes ENGRAM's own defaults. A project that
inherits and is set to restrict will exclude the inherited corpus for a query that is about
something else. Correct for a domain project — a trail-conditions brief should not surface ENGRAM
internals — and surprising if you read "inherited" as "always present".
Neither setting changes what a traversal returns, and that distinction is the one
worth keeping. prioritize and restrict both govern ranking —
what a query surfaces, and in what order. Reading the brief itself is a different operation: no query, no
score, every passage of every declared article. Part 5 shows how to ask for it, and because it does not rank,
authority_policy does not affect it at all.
8. Part 5 — What the agent does differently
Nothing. That is the point, and it is worth verifying rather than believing.
recall(query="how does entity resolution work?",
active_repo="git@github.com:you/your-repo.git")
The agent passes active_repo because that is how its project-scoped memory stays separated per
project. That same declaration is what resolves the brief. There is no authority parameter, nothing to add to
the agent's system prompt, and nothing to keep in sync when you change the brief later.
Which is the real payoff: you set ground truth once, and every agent that works in that project is anchored by it — including agents provisioned after you declared it, and agents someone else on the team runs. A new agent starts out already knowing how the system behaves, instead of inferring it from the code in front of it.
Asking for the whole brief
Ranking answers "what is relevant to my question?" — which is not the same as "what am I bound by?", and the second cannot be derived from the first. A brief passage that happens to name none of your query's nouns will never rank, however load-bearing it is, and a good half of a typical design document is in that state.
So recall can return the brief alongside the ranked results:
recall(query="...", active_repo="...", include_brief="manifest")
include_brief | Returns |
|---|---|
"auto" (default when you declare a project) | the whole brief when it fits, otherwise its structure |
"manifest" | article and section titles, with passage ids |
"full" | every passage of every declared article |
manifest is the one to reach for first: it is cheap, and it tells the agent what exists so it can
ask for the parts it needs. full is the traversal Part 4 referred to — the complete declared
set, unranked.
RECALL_CONVERGENCE_ENABLED. With it off, recall
takes the legacy path and does not pick up the brief — chat still does. If agents seem not
to inherit while the KB clearly does, check that flag before checking anything else.
9. What this gives you
- One place to be right. Correct the brief, and every agent in the project is corrected — no redeployment, no prompt edits, no drift between agents provisioned at different times.
- A defensible answer to "where did it get that?" The brief is readable; retrieval preferring it is a property of the project, not of a prompt someone wrote once.
- The same set the accuracy checks use, so an article contradicting your ground truth is caught before it lands in your knowledge base.
Appendix — Gotchas, ranked by how much time they'll cost you
PROJECT_AUTHORITY_ENABLEDis off. Everything below it is inert, and nothing complains. Check this first, always.- Absent inheritance reads as false. A project older than the flag inherits nothing until someone says otherwise.
- A private article is refused, not warned about. Publish first. The refusal is protecting you from a silent no-op.
- Two projects on one repo resolve to neither. Fail-closed is deliberate; the fix is to have one project per repo, not to retry.
restrictdoes not restrict documents or memories. Article arm only.restrictexcludes inherited defaults on off-topic queries. Usually right, occasionally startling.RECALL_CONVERGENCE_ENABLEDoff ⇒ agents don't inherit, but chat does. The one asymmetry in the feature.article_ids=[]clears the brief. It is the documented way to clear, which makes it an easy accident in a script that builds the list dynamically.