What your agents inherit

A hands-on ENGRAM walkthrough · August 21, 2026

Every agent you connect to ENGRAM already reads something as authoritative. This is how to find out what, and how to choose it deliberately.

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.

Background: Projects §2 — Ground Truth.
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:

3. Prerequisites

RequirementNotes
An ENGRAM account + MCP connectionClaude Code, Claude Desktop, or any MCP client
PROJECT_AUTHORITY_ENABLED onoff by default; an administrator flips it in Admin → Settings. Off, everything below is inert
An article you'd call authoritativeyour project must be able to read it — see Part 4
A projectif 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 backWhat it means
a list of articlesthe project declares its own ground truth
an empty set, and the project inheritsthe agent is reading ENGRAM's corpus, not yours
an empty set, no inheritanceno 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.

An absent 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:

  1. PROJECT_AUTHORITY_ENABLED is off;
  2. the project declared nothing and inherits nothing;
  3. 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:

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 articlesranked upthe only articles returned
Other visible articlesstill returned, ranked normallyexcluded
Documents, memories, code sessionsunaffectedunaffected
Use it fora report that must be informed by ground trutha 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_briefReturns
"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.

One caveat, because it decides whether this works at all. The agent-side arm goes through the shared ranking core, which is gated on 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

Appendix — Gotchas, ranked by how much time they'll cost you

  1. PROJECT_AUTHORITY_ENABLED is off. Everything below it is inert, and nothing complains. Check this first, always.
  2. Absent inheritance reads as false. A project older than the flag inherits nothing until someone says otherwise.
  3. A private article is refused, not warned about. Publish first. The refusal is protecting you from a silent no-op.
  4. Two projects on one repo resolve to neither. Fail-closed is deliberate; the fix is to have one project per repo, not to retry.
  5. restrict does not restrict documents or memories. Article arm only.
  6. restrict excludes inherited defaults on off-topic queries. Usually right, occasionally startling.
  7. RECALL_CONVERGENCE_ENABLED off ⇒ agents don't inherit, but chat does. The one asymmetry in the feature.
  8. 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.
Companion reference: Projects §2 — Ground Truth — when to use it, declaring a project's ground truth, prefer vs bind, and the corpus you already have.