Skip to main content

Module store

Module store 

Source
Expand description

Where basis’s conversations are persisted, and how they are scoped.

mentra persists every agent to its runtime store — since basis 0.7 the file-backed one, plain files under one root, no database (ADR-0023; upstream mentra#28) — and tags each record with a runtime identifier. basis uses that tag to answer one question: which conversations belong to this workspace? — which is what ACP’s session/list asks and the only reading of “my sessions” that is both honest and useful, since ACP scopes a session to a cwd from the moment session/new opens it.

§Why the workspace path, verbatim

mentra’s default identifier is the literal string "default", and its default store is one shared root. Listing under "default" would therefore enumerate the agents of every mentra program on the machine — worse than returning nothing, because a client would offer a user conversations that are not theirs.

The identifier is the canonicalized workspace path with a basis: prefix. No hash: the identifier never becomes a filename — mentra keeps it as a field of each agent’s agent.json and filters listings by comparing it — so every character survives, and a readable value is one a person debugging the store can understand by opening the file. The prefix keeps basis’s records from ever colliding with another program’s "default".

Changing the identifier does not move the store — mentra’s default path is independent of it — so nothing already written is lost. Records created before this scheme carry "default" and do not appear in any workspace’s list, which is the correct answer for a conversation whose workspace was never recorded. They are not stranded either: mentra loads an agent by id alone, so resuming one still works, and it re-tags itself the next time it persists. WorkspaceBuilder::open is where the tag is set, and where that ruling is written down.

One caveat since ADR-0018: mentra fixes the tag per runtime at build time, so only a workspace on its own private runtime — every Workspace::open(path), the CLI, the free functions — tags rows with its path. A workspace on a shared Runtime mints rows tagged "basis:runtime" until mentra grows a per-session override; those rows stay out of every per-workspace list (the "default" ruling above, applied again) and re-file themselves the first time they persist under a runtime that knows their workspace. Runtime’s mint is the one line that changes when the override lands.

§Where the files go

mentra’s default directory is keyed by the process’s current directory, not by the workspace basis opened, so every program started from one place shares one store root whatever workspace it went on to open — including every test binary in one cargo test. RuntimeBuilder::with_store_dir is how a caller says otherwise, and list_in is how the same caller reads back what it wrote. The directory the caller names is the store’s root — mentra lays agents/, rules.json and runs.jsonl inside it — and the root is bound to the store in exactly one place, store_in, because two places would eventually disagree and a conversation written under one root and looked for under another is simply missing.

§When the directory holds a database instead

basis 0.6 and earlier kept conversations in runtime.sqlite under the same directory. This build links no SQLite and does not migrate (ADR-0023’s E2 precedent): opening, listing or deleting against a directory that holds one is refused with RunError::LegacyStore, which names the two ways forward, rather than starting an empty file store beside data it cannot see — the refusal is refuse_legacy_store, and every path that opens a store the caller pointed somewhere runs it.

§When there is no file

RuntimeBuilder::with_ephemeral_history answers where with nowhere, and opens mentra’s in-memory store instead. Nothing in this module can see one of those conversations: there is no file for list_in to read and no row for list to filter, whichever directory either is pointed at. One file still gets written even then — a compaction snapshot, which mentra persists without consulting the store — and it goes to a per-runtime directory under the OS temp directory, which is as close to nowhere as that file gets. Everything else below is about the durable case.

Structs§

PersistedSession
A conversation mentra has on disk, as basis reports it.

Functions§

default_directory
The directory mentra keeps basis’s conversations in, for a caller that wants to say where the history lives.
forget
Removes a conversation from the default directory’s store for good.
forget_in
The same, for conversations kept somewhere of the caller’s choosing.
list
Every conversation persisted for workspace, most recently used first.
list_in
The same, for conversations kept somewhere of the caller’s choosing.
runtime_identifier
The runtime identifier for conversations in workspace.