Give your agents a shared memory — scoped per project

A hands-on ENGRAM walkthrough · July 11, 2026

Why this exists. The companion blog post — A Brain Between Sessions — and Between Agents — shows the payoff: one recall returning an agent's own memory alongside a teammate's, and the same call isolating by project. This walkthrough lets you reproduce it end-to-end on your own ENGRAM instance, using the real tools an operator and an agent use — the Admin UI plus the MCP verbs remember, share_memory, and recall.

1. What you'll build

By the end you'll have stood up two agents, shared one agent's project memory to the other, and watched cross-agent recall respect project boundaries. The finished picture:

One screenshot-worthy outcome: recall(…) returns items tagged via:own and via:shared; the same call under a different repo makes the borrowed items vanish.

2. Concepts in 60 seconds

Four ideas are all you need to follow the flow:

3. Prerequisites

Two identities, two PATs. The magic of this demo only works when the client is authenticated as the agent whose memory you're reading. You'll drive some steps as Atlas and the payoff as Nova — each with its own Personal Access Token.

4. Step 1 — Provision the two agents (Admin UI)

In the Admin UI, go to Users → Agents and create the two agents:

Mark both agents shareable — this makes them valid list_grantees targets, which agent↔agent sharing requires (agent grants are gated; a non-shareable agent grantee is rejected). Then mint a PAT for each: Nova's PAT drives recall; Atlas's drives remember and share_memory.

Admin UI Agents roster showing two agent users — Nova with role coder and Atlas with role orchestrator — both marked shareable, each with a Personal Access Token control
Admin → Users → Agents · the cast of the tutorial — Nova (coder) and Atlas (orchestrator), both shareable.
Who can create agents? An Administrator can, and so can anyone you grant the Agent Manager role (Admin → Users → set role to Agent Manager) — a capability specifically for standing up and managing the agents they own, without full admin. See the Agent Manager section of the docs.

5. Step 2 — Register the projects

Each project is a :Project with a unique repo_url — that URL is what an agent declares via active_repo, and resolution is fail-closed on a single match. For this walkthrough the two projects are:

github.com/pvelua/checkout-service
github.com/pvelua/billing-service

A repo becomes a project in ENGRAM the first time work is filed under it — a code-session ingest from the ENGRAM watcher, or the first article/memory anchored to that repo. Once the :Project exists with its repo_url, an agent can declare it by passing active_repo (which ENGRAM resolves to the internal active_project_id) on any recall.

6. Step 3 — Atlas records project decisions (remember)

Authenticated as Atlas (his PAT), record the architectural decisions and constraints — one memory per project, scope="project", anchored to that project's work:

remember(
  content="Decision: refresh tokens are single-use — rotate on every call, never cache the prior token.",
  scope="project"
)

remember(
  content="Constraint: access-token TTL is fixed at 15 minutes by compliance — do not extend it.",
  scope="project"
)

remember(
  content="Billing must be idempotent per payment-intent id.",
  scope="project"
)
No hand-wiring. remember extracts and embeds the entities for you, so a later natural-language recall(query=…) resolves without you naming entity ids by hand. The tool does it.

7. Step 4 — Nova records her own notes (remember)

Now, authenticated as Nova, record her working notes. Some are project-scoped; one is user-scoped so it persists across every project she works in:

remember(
  content="Capped the token-refresh retry loop at 3 attempts with jittered 250 ms → 1 s backoff.",
  scope="project"
)

remember(
  content="checkout depends on auth-client v2.3; v2.4 changes the refresh-token signature.",
  scope="project"
)

remember(
  content="Prefer structured logging with request-id correlation across services.",
  scope="user"
)

The contrast is the whole point: user-ring = always with me; project-ring = only in that project.

8. Step 5 — Atlas shares each project to Nova (share_memory)

Back as Atlas, share each whole project to Nova, bound to that project:

share_memory(
  selector_kind="project",
  selector_id="<checkout-service project id>",
  grantee="nova",
  bound_scope="project"
)

share_memory(
  selector_kind="project",
  selector_id="<billing-service project id>",
  grantee="nova",
  bound_scope="project"
)

What each argument does:

Confirm the grants landed with list_shares(direction="by") — you'll see both project grants to Nova.

9. Step 6 — Nova recalls (the payoff) 🎯

Authenticated as Nova, ask one question while declaring the checkout project:

recall(
  query="auth token refresh retry strategy",
  active_repo="github.com/pvelua/checkout-service"
)

In a single call Nova gets her own notes and Atlas's decisions — each tagged with where it came from. Two rows are via:own (hers), two are via:shared (Atlas's):

ENGRAM recall for 'auth token refresh retry strategy' in the checkout-service project returns a four-row table; two rows tagged via: own (owner Nova) and two tagged via: shared (owner Atlas)
One recall, two sources. Working in checkout-service, Nova gets her own notes (via:own) alongside Atlas's decisions shared to her (via:shared).

Now run the exact same query — only the repo changes:

recall(
  query="auth token refresh retry strategy",
  active_repo="github.com/pvelua/billing-service"
)

Atlas's checkout-bound decisions are simply gone — they were shared bound to the checkout project, so they don't cross over. Nova's own cross-project note stays:

The same ENGRAM recall query run in the billing-service project returns fewer rows, all via: own (Nova); Atlas's checkout-scoped shared rows are absent
Same question, different project. Move Nova to billing-service and Atlas's checkout decisions disappear. Shared memory that respects boundaries.
Read the via tags aloud. Which memories are hers (own), which are Atlas's (shared), and why the set changed when only the declared project changed. That's the entire cross-agent story in one before/after.

Note: recall returns owner_user_id as the raw user id (not a display name), so in a live transcript you'll see the ids — caption them Nova / Atlas.

10. What just happened (peek under the hood)

11. Going further

Hand-off

Instead of a standing share, an agent can hand a batch of memories to a peer for review: commit_task_memories(…, hand_off_to="<peer>") creates a recap the receiver owns. The receiver reviews it — list_inbox() to find it, then accept_memories(recap_id) — and a grant-back keeps the original worker's recall intact.

Owner-ring widening & scope badges

In the Memory Explorer, every memory card carries an owner-ring scope badgetask-private, conversation, or project (user-wide memories carry none). The badge makes a memory's reach legible, so widening it becomes a deliberate, visible act rather than a silent default.

ENGRAM Memory Explorer long-term memory list; memory cards each show an owner-ring scope badge — TASK-PRIVATE, CONVERSATION, PROJECT — while a user-scoped preference card has no badge
Every memory carries its reach. Scope shows as a badge — task-private, conversation, project — while user-wide memories carry none.

Discovery & audit

Revoke and re-check

Sharing is reversible. revoke_share(share_id) soft-revokes a grant — run the same recall afterward and the borrowed item is gone. Try it: revoke the checkout grant, recall again under checkout, and watch Atlas's decisions drop out.

12. Troubleshooting

recall comes back empty

a shared memory isn't appearing

Invalid or revoked API key

Appendix — reproduce it deterministically

The exact two-agent world used for the screenshots is seeded by scripts/seed_cross_agent_recall_demo.py (idempotent). Because it writes memory directly rather than through remember, it also does two things the tool would otherwise do for you — a ChromaDB entity embed and a mention scaffold — so that a natural-language query= resolves. A reader who follows Steps 3–4 with remember gets both for free.

Wiring your client. Point an MCP client at your instance authenticated as the agent. For example, with Claude Code:
claude mcp add --transport http engram-nova https://<your-engram-host>/mcp/ \
  --header "Authorization: Bearer engram_pat_…"
Then run the two recall calls from Step 6 back to back for the transcript.