1. Executive Summary
Bifrost is a self-hosted agent runtime engine — AGPL-3.0, Rust 1.75+ with a Python side, 11,091 lines of Rust and 121 test functions, part of a named multi-repository platform in which it plays orchestrator: receive a request, classify intent, delegate to tool and agent workers, assemble the answer, under guardrails it labels scope guard, tool allowlist, citation check, confidence gate and disclaimer.
Its memory is 148 lines. src/memory/ holds a manager and
a tool over memvid_core — Memvid
being a separate project this atlas reads in its own right — and the
whole of the storage decision is one function:
fn get_agent_path(&self, agent_id: &str, session_id: &str) -> PathBuf {
self.base_path.join(format!("agent_{}_session_{}.mv2", agent_id, session_id))
}
One file per agent and session. The swarm overseer commits a frame per completed turn — the user query and the agent's final answer concatenated, titled with the session id and a timestamp — and registers a search tool on the agent builder. Both are constructed from the same pair:
session_id.unwrap_or("anon")
The two ends agree, and that is the good part. The key an agent reads with is the key this runtime wrote with, so there is no asymmetry between write and read — the failure this shape usually has. A commit failure is logged at error level and swallowed, so a memory write cannot fail a user's turn.
The description and the key do not agree. The tool tells the model it is searching "your absolute long-term memory for past conversations, facts, or context you have stored using this tool". The store it reaches is scoped to the current session by filename, so past conversations means earlier turns of the present one. A new session opens a new file and starts empty.
The exception is the fallback. When no session id is supplied, both
paths use the literal "anon", so a tenant's session-less
turns accumulate in one shared
agent_{tenant}_session_anon.mv2 — the only configuration in
which the tool's description is accurate, reached by the absence of an
identifier rather than by a decision.
No marks. Scope here is a filename, which is the physical-partition shape this atlas does not count as enforcement; nothing in the layer supersedes, retires or de-duplicates, and no delete path exists at all, so a fact restated ten times is ten frames. The mechanisms that would earn marks belong to Memvid, and are read there.
Two things a reader should know about the tree rather than the code.
The top level carries INTEGRATION_STATUS.md,
PHASE_A_SUMMARY.md, COMPLETION_SUMMARY.md and
a Docker build workaround — useful, because they say which parts are
finished before a reader goes looking. And src/ contains
rl_orchestrator.rs.backup and
rl_safe_deployment.rs.backup committed beside their
originals.
2. Mental Model
A frame is one turn, flattened into text.
A memory is a file, and the session is part of its name.
"anon" is where memory becomes long-term, by accident.
Diagram source
%% caption: the write and the read use the same tenant and session pair, so they agree — and both resolve to a per-session file, while the tool description promises memory across conversations
flowchart TB
REQ["a user request reaches the overseer"] --> RUN["classify intent, delegate to<br/>tool and agent workers, assemble"]
RUN --> TOOLREG["register MemvidSearchTool::new(<br/> manager,<br/> tenant_id,<br/> session_id.unwrap_or(\"anon\")<br/>)"]
TOOLREG --> DESC["tool description to the model:<br/>'Search your absolute long-term memory<br/>for past conversations, facts, or context<br/>you have stored using this tool.'"]
RUN --> DONE["turn completes"]
DONE --> COMMIT["commit_memory(<br/> tenant_id,<br/> session_id.unwrap_or(\"anon\"),<br/> 'User Query: … Agent Response: …'<br/>)"]
COMMIT --> PATH{"get_agent_path"}
TOOLREG --> PATH
PATH -->|"session present"| PERSESS[("agent_{tenant}_session_{id}.mv2<br/>— a new session opens a new file<br/>and starts empty")]
PATH -->|"session absent"| ANON[("agent_{tenant}_session_anon.mv2<br/>— every session-less turn for this<br/>tenant pools here; the only case<br/>where the description holds")]
PERSESS & ANON --> MV["Memvid does the storing and the search"]
COMMIT -.->|"on error: tracing::error! and continue —<br/>a memory write cannot fail the turn"| DONE
NOTE["no delete, no supersede, no dedup:<br/>a fact said ten times is ten frames"] -.-> MV3. Architecture
| Area | Role |
|---|---|
src/swarm_engine/overseer.rs |
The orchestration loop, and both memory call sites |
src/memory/memvid_manager.rs |
Eighty-eight lines: path, create-or-open, commit |
src/memory/tool.rs |
Fifty-eight lines: the agent-facing search tool |
src/rl_*.rs |
Governance voting, audit trail, safe deployment, self-eval |
src/retrieval, src/agents.rs |
Retrieval and the agent registry |
dashboard/, k8s-manifest.yaml |
A UI and a deployment manifest |
4. Essential Implementation Paths
src/memory/memvid_manager.rs:19-21 — the whole scoping
decision, in three lines.
src/swarm_engine/overseer.rs:308-316,
:905-912 — the two call sites, and the fallback they
share.
src/memory/tool.rs:32-40 — what the model is told the
tool does.
5. Memory Data Model
A frame is
format!("User Query: {}\nAgent Response: {}", query, final_answer)
with a title carrying the session id and an RFC 3339 timestamp. No
structure beyond that: no speaker separation once stored, no facts
extracted, no provenance, no importance, no state.
6. Retrieval Mechanics
One tool, one query string, and Memvid's own search behind it. The runtime adds no ranking, filtering or budgeting of its own.
7. Write Mechanics
One append per completed turn, best-effort. Errors are logged and dropped, which is the right priority ordering for a user-facing turn and means nothing counts the frames that were lost.
8. Agent Integration
The tool is registered on the agent builder alongside the others,
with a bypass_tools path that calls the memory search
directly rather than letting the model choose it. That second path is
worth noting: it is how the runtime guarantees a memory lookup happens
even when the model would not have asked for one.
9. Reliability, Safety, and Trust
The memory layer carries none of its own. The runtime's guardrails, RL audit trail and governance voting are separate subsystems, not read here, and none of them touch the memory path.
10. Tests, Evals, and Benchmarks
121 test functions across the Rust tree, plus shell and Python test scripts at the top level. No test was found covering the memory wrapper. Nothing was built or run for this reading.
11. For Your Own Build
Make the tool description match the key. "Your absolute long-term memory" over a per-session file teaches a model to trust a recall that cannot span the conversations it names — and the fix is either a word in the description or a field out of the filename.
Do not let a missing identifier choose your scope.
unwrap_or("anon") turns an absent session into a shared
bucket; whether that bucket is a leak or the only working configuration
depends on who else is in it.
Keep the write key and the read key in one place. This code gets it right by constructing both from the same expression a few lines apart, which is the cheapest version of that guarantee.
12. Open Questions
Whether session_id is ever None in
practice. The fallback exists at both call sites, and what supplies the
id was not traced.
Whether the per-session scoping is intended. Nothing in the tree explains the choice, and the tool description argues against it.
What the .backup files were. Both sit beside their
originals in src/.
Appendix: File Index
| Path | What to read it for |
|---|---|
src/memory/memvid_manager.rs:19-21 |
The scoping decision, entire |
src/memory/tool.rs:32-40 |
The promise made to the model |
src/swarm_engine/overseer.rs:308-316,
:905-912 |
Read key and write key, agreeing |
History
2026-09-16 — 936957b3…
— first reading, at a commit dated 16 September 2026. Screened before
opening, from a shallow clone: seven files scanned, no auto-run
surfaces, no build-time execution points, one unpinned surface and five
dependency files inside the seven-day cooldown. Nothing was installed,
built or run.