Associative retrieval does not fail when a question has a correct answer. It returns the plausible neighbour, states it confidently, and gives you no signal that it did. So ENGRAM now lets a project declare which documents are authoritative – and every agent working in that project inherits that declaration without being told it exists.
Until now this series has been about memory that finds what is related. Entity-seeded Personalized PageRank, a query blended with what you already care about, a graph that gets more personal as you fill it in. That is the right instrument for research, for exploration, for the half-formed question you cannot phrase precisely yet.
It is the wrong instrument for “what does this endpoint return?”
The distinction had been sitting in my notes for months as a retrieval-quality complaint. It turned out to be a missing primitive, and the release that closed it (Ground Truth, shipped in v1.15 and live on PROD) is the first ENGRAM feature that is not really about memory at all. It is about documents – which of them count, who gets to say so, and how little an agent needs to be told for that to hold.
The plausible neighbour
“The failure mode, concretely.”
While building belief revision, I kept noticing that anchored recalls returned real, well-written design articles that had nothing to do with what I had asked. Not junk – genuine documents that happened to share entities like token budget or memory-service with the query. I filed it at the time as inherent to entity-seeded retrieval and moved on.
That diagnosis was too generous to itself. The problem is not noise in the ranking; it is that nothing in the system had ever been told which documents are supposed to win.
Consider two coding agents on the same project. Both recall against the same knowledge base. Both get something relevant. Neither is obviously wrong, and neither has any way to know it is working from a different reading of the same design than its counterpart. You discover the divergence when the code disagrees, which is the most expensive moment available.
What made this structural rather than incidental is a consequence of how a new agent bootstraps. A freshly provisioned agent is not staring at an empty graph – it can resolve every entity extracted from public articles, which is exactly how ENGRAM avoids a cold start. So, at present, a brief written for a team has to be public for the team to resolve it. And the moment it is made public, its entities join the same undifferentiated pool as every other public artifact: other teams’ briefs, ambient research notes, stale onboarding content. Planned project-scoped visibility will remove that requirement.
Write isolation had shipped a release earlier – a worker anchors its findings to a project and they stay there. The read counterpart did not exist. There was no way to say these documents are ground truth, prefer them.
Read before you write
“What your project is already grounded on, concretely.”
The instinct with a new feature is to configure it. The more useful first move is to ask what your project already resolves, because the answer is frequently not what people expect:
get_project_authority(project="engram-personal-kb")
Three shapes of answer come back, and all three are legitimate. A list of articles means the project declares its own ground truth. An empty set with inheritance means the agent is reading ENGRAM’s corpus rather than yours. An empty set with no inheritance means there is no ground truth at all and retrieval is ordinary and associative – which is not a failure, and is the state every project was in before any of this shipped.
The middle answer is the one worth dwelling on, because inheritance is silent. ENGRAM now ships two of its own reference articles as a ground-truth corpus, held in a single system-owned project, and each user’s default project draws on it by flag rather than by copy. So “how does recall actually work?” asked in a default project is answered from ENGRAM’s own reference instead of from whatever you happened to save. Publishing a corpus article is one write rather than one write per user, and a new account inherits at creation instead of needing a seeding step somebody can forget.
One rule surprises everybody, and it is deliberate:
An absent
inherit_engram_defaultsmeans does not inherit. Not “inherits”.
The friendly-looking default is the wrong one. Every project that existed before the flag has no value for it, so absent-means-inherit would have unioned ENGRAM’s corpus into briefs that were narrowed on purpose – a project declaring three documents under a strict policy would silently have acquired two more. Widening a brief is a correctness bug wearing a feature’s clothes. So new projects get inheritance at creation, existing per-user defaults were switched on by a backfill somebody had to run and check, and a deploy never widens a brief.
Worth stating for anyone who has been with ENGRAM since the early releases: the onboarding articles that make a new account useful are not the corpus. Those answer how do I use ENGRAM; the corpus answers how ENGRAM actually works. They overlap in subject and stay separate anyway, because changing ownership of a seed article would also misrepresent who wrote it.

The agent is already grounded — on ENGRAM’s documentation rather than on anything its owner wrote.
Two features, not one
“What is separately enabled, concretely.”
Ground Truth is two mechanisms sharing one declaration, and conflating them makes the rest read as a single blurry thing.
The first is retrieval preference: queries asked in a project’s scope prefer, or are bound to, that project’s declared articles. The second is article verification: articles proposed into the project are checked against those same articles before they land in the knowledge base.
They are enabled separately, per project, both off by default. The dependency between them is real rather than administrative – the verification corpus is the declared set, so enabling the check without the declaration would be enabling a gate with nothing behind it.
One consequence follows immediately and matters later: corpus articles are not themselves checked. Verifying the reference against itself is circular. That is correct, and it is also a flaw, which the second half of this post is about.
Prefer, or bind
“The one setting that changes behaviour most, concretely.”
set_project_authority(
project="project:…",
article_ids=["article:…", "article:…", "article:…"],
policy="restrict"
)
Under prioritize (the default) declared articles are ranked up and everything else is still returned, ranked normally. A question the brief cannot answer still gets a reasonable answer from the wider knowledge base. Fail-open.
Under restrict, the declared set is the only article content returned, and retrieval reports when the brief does not cover the question rather than reaching for a neighbour. Fail-closed.
The difference is between being informed by a document and being bound by one, and whoever declares the scope declares the policy. A report that must be informed by ground truth wants the first. A team building against a contract wants the second.
Two things people get wrong. restrict scopes the article arm only – memories, documents and code sessions are untouched, so it focuses a worker rather than lobotomising it, and reaching for it to make an agent “only use approved sources” will not do that. And under restrict, “ambient” includes ENGRAM’s own inherited defaults: a domain project set to restrict will exclude the inherited corpus on an off-topic query. That is right for a project about trail conditions, which should not surface ENGRAM internals, and startling if you read “inherited” as “always present”.
The declaration replaces rather than appends. Each call defines the complete set, re-declaring the same ids changes nothing, and an empty list clears it – idempotent by construction, and safe in a setup script that runs more than once.
Two machines, one contract
“The whole feature in a single incident, concretely.”
I built a use case to test whether ENGRAM memory could actually carry collaboration between agents that share nothing but the memory. Two coding agents (Claude), two machines, two Git repositories, two identities in ENGRAM with their own access tokens – reaching the same ENGRAM instance and nothing else in common.
The project is Trailhead: is this trail worth walking today? trailhead-api serves trails and their current condition; trailhead-worker polls weather, computes observations and reports them. The API agent works on a MacBook and owns its repository. The worker agent works on a Mac mini and owns its own. Neither commits to the other’s, ever.
The brief is three public articles, declared under restrict, with ENGRAM’s own corpus fenced off:
- Trailhead – System Design. What it is, why two services, the data model, the rules.
- Trailhead – API Contract. The shared boundary – neither agent changes it alone.
- Trailhead – Working Agreement. Stack, what to record in memory, git discipline, definition of done, deploy discipline.
Each agent’s first session opens by proving its grounding rather than by writing code. Recall the brief; check that every brief document comes back marked as authority; summarise the contract back from the brief only, because a fresh repository contains nothing to contradict a confident invention. One call settles identity, token, project declaration and ground-truth retrieval simultaneously. An agent that gets this wrong does not error – it recalls nothing useful and builds against whatever it can infer, which is the failure this whole feature exists to prevent.

restrict, with inheritance switched off. Compare it with the call above: the same tool, the same agent, two projects that resolve entirely different ground truth. ENGRAM’s own corpus is fenced off, so the brief is exactly the three documents the team agreed on.
Then the interesting thing happened, and it is recorded in the contract’s own revision log.
The worker agent could not poll weather. The trail object as specified carried no latitude or longitude, and the worker by design holds no database credentials – the catalogue is the only channel through which a trail’s position can reach it. The omission had gone unnoticed on the API side because the contract described the trail object with an example rather than a normative field list, and the example happened to include coordinates while the specification did not require them.

A worker defaulting to
(0, 0) would post plausible-looking weather for a point in the Gulf of Guinea.So the worker agent proposed the change as a memory, stating what it needed and why. The API agent assessed it and agreed. I approved. The contract was revised – coordinates added to the trail object, and a normative field table added alongside them, because inferring a shape from an example is precisely what allowed the omission to survive. The change was additive rather than breaking, so no new API version was needed: the contract document moved to its second revision while the served interface stayed at /v1.

It concedes on the axis that matters, and identifies the example-instead-of-a-field-list as the root cause rather than the symptom.
Two things about that are the point of this post.
First, the disagreement was resolved in the document, not in either repository. Code that contradicts the contract is wrong even when it works, because the other agent is building against the document rather than against your repository.
Second, nobody re-declared anything. A declaration records an article’s current version, and retrieval walks the version chain forward at query time, so both agents were reading the revised contract on their next recall. The brief cannot silently lose an article to an edit, and it cannot silently keep serving a superseded one.
The app was built, tested and works.

v2, with the version it replaced preserved beneath it. Revising a brief document supersedes rather than overwrites, so the earlier version stays in the chain.
What ranking cannot answer
“The question retrieval was never asked, concretely.”
Ranking answers “what is relevant to my question?”. It cannot answer “what am I bound by?”, and the second is not derivable 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. The section that says never force-push main does not surface on a query about condition rules, and it still governs.
So recall can return the brief alongside the ranked results: a manifest of article and section titles with passage ids, cheap enough in tokens to ask for by default and enough to tell an agent what exists so it can request the parts it needs; or the full set, every passage of every declared article, unranked.
That is a traversal rather than a retrieval. No query, no score, no possibility of returning “nothing relevant”. Which means (and this turned out to matter far more than it looked) the brief a reader gets by traversal does not depend on authority_policy at all.
The reference stops being a build artifact
“Where the accuracy check gets its corpus, concretely.”
ENGRAM has checked auto-drafted articles against a reference for several releases. The check itself was sound: it verifies claims before a draft reaches your inbox, computes its verdict from per-claim findings rather than the model’s own summary, and fails closed to queued rather than to a silent pass.
What did not scale was where the reference came from. It was a hand-maintained document compiled by a script into a string constant and baked into the service image. Three consequences followed, and I felt all of them. Correcting one line of ground truth was a code change, a commit and a deploy. There was only one reference, and it described ENGRAM’s own internals, so every draft was checked against ENGRAM no matter what the draft was about. A Trailhead article had nothing it could be verified against. And the same knowledge now lived twice (as a baked constant and as public articles) which is a poor arrangement for a feature whose entire purpose is being right.
Now the reference is the project’s declared set, retrieved at call time. Updating it is set_project_authority. No rebuild, no second copy.
The obvious objection is the one worth answering, because it is what the traversal above resolves. If the check obtained its reference by ranking, then a ranking cut-off would decide what the check sees, and the same claim could come back supported on one run and unverifiable on the next purely from retrieval noise. A verdict that moves with ranking cannot gate anything. But the check does not rank – it walks the declared article ids to their current passages, so it reads the whole declared set irrespective of the project’s policy. What a query prefers and what a check verifies against are governed by different things, deliberately.
A corollary worth stating in guidance rather than enforcing in code: declared sets used for checking should stay small. The check reads the whole declared set rather than a ranked slice of it, which is what makes its verdicts stable – and a brief of five focused documents is easier to keep true than one of fifty.
The check is not a cheap call, and it is now measurably less expensive than it was. The reference is the large, stable, repeated part of every invocation — the exact shape prompt caching exists for. Measured live against the API: a stable prefix of 38,274 tokens, created on the first call and read from cache on every call after it, with only the draft charged as fresh input. Caching only pays if the cached block is byte-identical on every call, which is why the reference goes inside it and the draft stays outside. Put the draft inside and every call has a different prefix, so nothing ever hits the cache — and because writing to cache costs more than sending plain input, the bill ends up higher than if caching had never been switched on. The failure is silent: the check still works, it just quietly costs more. So the ordering is pinned by a test.
The highest-privilege surface in the system
“The flaw two correct decisions compose into, concretely.”
Corpus articles are not themselves checked, because checking the reference against itself is circular. And an agent may revise an article that a project it belongs to has declared as ground truth — which it must be able to do, or the team’s brief can only ever be maintained by a human, and the Trailhead contract revision above could not have happened.
Both are right. Together they are a flaw: an agent that cannot get a confabulation past the check can instead revise a corpus article to make the confabulation ground truth (unchecked) after which everything downstream validates against it.
The answer is not “humans only”, which would break the case the feature exists for. It is that a revision of a corpus article is checked against the corpus as it stood before that revision – the prior chain head. A genuine update passes. A revision contradicting established ground truth is caught. It costs one extra reference assembly on a rare path.
And a verdict is not the only acceptable gate. Trailhead ran exactly this shape by hand: the worker proposed a contract change with its reasoning, the API agent assessed it and conceded, and the revision was accepted by the team. A recorded human or team acceptance satisfies the same gate – not as a bypass of the model’s judgement, but as the same gate with a different and often better judge.
There is one more piece, and it is the part of a reference that a set of true documents cannot express. The corpus also carries a negative list: claims asserted to be false. In ENGRAM’s own case that means things models reliably invent about it — an “Article Rank” algorithm, a self-querying model that writes Cypher, Neo4j-native vector storage, enterprise “Skill Packs”. None of these exist, and a set of articles saying what is true does not naturally say this does not exist.
The asymmetry between the two halves is deliberate and should not be tidied into symmetry:
positive corpus → REPLACES (a project's own corpus displaces ENGRAM's defaults)
negative list → UNIONS (ENGRAM's always applies; a project's adds to it)
A domain project must be able to displace ENGRAM’s internals as its truth, or every Trailhead claim gets checked against ENGRAM’s architecture. But nobody ever needs to re-permit a known falsehood, so negatives compose safely and additively.
The negative list is a distinct edge type rather than a flag on the positive one, and the reason is worth the paragraph. With a role property, every query reading the positive set would need a filter appended excluding the negative role – and omitting that filter in one place means the list of known falsehoods gets treated as ground truth, which is the worst failure this feature can produce, caused by one missing WHERE clause. A distinct type makes it unreachable from the positive query by construction.
What this still cannot do
Ground truth is live on PROD at v1.15.4. The Trailhead use case test run described above was on the development environment.
Ground-truth articles must be public, and a private one is refused rather than warned about. This is a real cost: a brief you want your team to work from has to become visible to everyone. The refusal is still the right behaviour – a private brief would be invisible to the very agents it exists to anchor, so you would get a success response and no effect. A three-value model (private · project · public) is planned, and until it lands, public is the only setting that makes an article usable as ground truth.
Nothing infers which project an agent is working in. An agent reaches the brief only by declaring the project on each call, and declaring only its repository resolves to the per-repo project that session capture creates – which does not hold the brief. The agent then finds no ground truth, recalls nothing useful, and reports no error. The durable workaround today is a line in the repository’s CLAUDE.md, which a coding agent reads at session start and which every machine inherits by pulling. A per-agent home project is the proper fix, and it comes with the project-model work.

CLAUDE.md in the API repository: the brief lives in ENGRAM, and the agent declares the project on every memory call. The repository holds no ground truth of its own — only the pointer to where it lives, and a warning about the one way to get it wrong.
Projects do not yet have a full settings surface. Every decision described here adds a per-project setting, and today a human can only create a project from the Projects panel. A setting reachable only over MCP is a setting only an agent can use, which inverts the intended relationship – the human owns the project and delegates work to agents, not the reverse. That surface is in progress alongside the rest of the project-model work.
Every post in this series so far was about retrieval that finds what is related. This one is about a brief that declares what is authoritative, and a check that establishes what is verified — three different questions that were being answered by one mechanism, and now are not.
The part I did not expect: none of it is visible to the agent. There is no parameter to pass, nothing to add to a system prompt, and nothing to keep in sync when the brief changes. An agent declares which project it is working in (which it already does to keep its memory straight) and the brief comes with it. Set ground truth once, and every agent working in that project is anchored by it, including the ones provisioned afterwards and the ones somebody else on the team runs.
Which is also why read before you write is the honest first instruction rather than the cautious one. Something is already authoritative for your agents. The question is only whether you chose it.
ENGRAM is in private beta. If you would like to try it, get in touch.
Companion walkthrough: What your agents inherit, which covers reading a project’s brief, the three inputs that produce it, and choosing between prefer and bind.
For the background this builds on:
- What I Believed Last Tuesday: ENGRAM Learns to Change Its Mind — belief revision and the supersession chain
- No Message Passed Between Them: ENGRAM Agents Coordinate Through Memory — agent coordination through shared memory
- What the Transcript Couldn’t Tell Me: ENGRAM Learns to Read What Actually Shipped