You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add native, backend-aware revision history for Basic Memory notes.
Cloud projects: use the object versions and point-in-time snapshots already provided by Tigris.
Local projects: use Git or Jujutsu as the revision store.
MCP/API: expose one revision-aware contract for historical reads, file history, comparison, and safe restoration.
This preserves the useful goals from #1154—visibility into changes, diff, and undo—without recording a second mutation log in the lifecycle-hook inbox from #997.
This is new product work. Basic Memory does not currently have the cross-backend revision abstraction, a local Git/JJ history backend, or revision-aware MCP tools. Existing database versions and checksums are synchronization and optimistic-concurrency state; they are not retained note history.
Why native revisions instead of mutation envelopes
A durable mutation envelope duplicates information already represented by versioned file content and creates another source of truth that must be retained, queried, repaired, and reconciled.
It is also incomplete for a local-first product: an envelope emitted from Basic Memory's write path cannot see edits made directly through Obsidian, VS Code, shell scripts, file synchronization, or other tools. A storage-backed revision source can cover both Basic Memory writes and externally edited files once the local watcher observes them.
The #997 inbox has a different job. It captures selected Claude/Codex lifecycle events with redaction, opt-in configuration, idempotency, and bounded operational concerns. It should not become the authoritative, permanent history of note content.
Revision history should answer:
What did this note contain at a particular revision or time?
What changed between two revisions?
Which files were added, removed, or modified across a directory or project?
Can I restore old content without erasing later history?
Actor trust, write authorization, and lifecycle telemetry are separate concerns and are not part of this issue.
Proposed architecture
Define a narrow revision-source capability in core and provide runtime-specific implementations:
MCP tools / CLI
↓
Typed API clients
↓
API and note services
↓
NoteRevisionSource protocol
├── TigrisRevisionSource (cloud)
└── Git/JJRevisionSource (local)
The contract should model capabilities, not provider internals. A revision reference is opaque to callers but carries enough scope information for the provider to validate it.
The exact Python and public MCP schemas should be reviewed before implementation. The important boundary is that core owns the behavior and response models while cloud/local composition roots provide the storage implementation.
Unsupported capabilities must fail clearly—for example, revision history is not available for this project—rather than silently returning current content or an empty history.
Revision reference semantics
A revision reference may identify:
an exact stored object/file revision;
a project-wide snapshot/commit; or
a timestamp resolved to the latest revision at or before that time.
The response should return the resolved, provider-issued revision identifier so callers can use a stable ref for subsequent reads or comparisons.
Provider-specific identifiers remain opaque:
Tigris exact object version IDs apply to a single path.
Tigris snapshot timestamps apply to notes, prefixes, and whole projects.
Git/JJ commit IDs apply to a project tree and can be narrowed to a path or prefix.
Timestamp resolution must be explicit and deterministic: each path resolves to its latest stored revision at or before the requested instant.
Cloud implementation: Tigris
Tigris snapshot-enabled buckets already retain historical object content. The cloud implementation should use that history directly instead of creating shadow revision rows or mutation records.
Historical reads
For one note or file:
Resolve the identifier to its current path when necessary.
Read the requested Tigris object version or the version at-or-before a timestamp.
Parse the historical Markdown/file content directly.
Return the resolved revision metadata with the content.
The current knowledge index describes now. Historical reads must not pretend that the current indexed observations and relations represent the historical graph.
File history
Return a cursor-paginated list containing at least:
revision/version ID;
timestamp;
size;
etag or checksum;
whether it is the latest revision;
deletion state when the provider exposes it.
Presentation layers may group rapid saves, but storage should preserve the original revisions. Coalescing is a UI concern unless a later retention policy says otherwise.
Recursive compare
A directory or project comparison can be computed cheaply from two at-version listings:
List the selected prefix at from_revision.
List it at to_revision.
Join entries by path.
Classify added, removed, and modified files; use etag/checksum inequality for modification detection.
Fetch file bodies only when a caller asks for a per-file text or semantic diff.
Restoring historical content must read the old revision and write it through the normal current write path. The restoration therefore creates a new head revision and never deletes or rewinds the intervening history.
The write should accept an expected current revision/checksum so a restore cannot silently overwrite a newer concurrent edit. A named snapshot before a bulk restore may provide an additional safety marker, but the underlying object history remains authoritative.
Local implementation: Git or Jujutsu
Local projects need a real revision source rather than emulated empty history.
The implementation must:
record Basic Memory writes;
record direct filesystem edits after the project watcher observes them;
preserve revisions without requiring GitHub or any remote;
avoid changing the user's active branch, staging area, or normal commit history without explicit opt-in;
expose stable revision IDs and timestamps through the same core contract;
remain recoverable with standard Git/JJ tooling;
fail loudly if history is disabled or unavailable.
Candidate designs
Git object database with a Basic Memory-owned ref
Create trees/commits through Git plumbing.
Advance a namespaced ref such as refs/basic-memory/history/<project-id>.
Do not checkout, stage, or commit onto the user's branch.
Sidecar bare Git repository
Store the revision database under Basic Memory's application data rather than inside the note project.
Avoid interfering with an existing repository and support projects that are not already Git repositories.
Jujutsu-backed history
Use JJ's automatic working-copy snapshots and Git-compatible storage.
Evaluate the operational cost of requiring or bundling the jj executable.
The first implementation should be chosen by a focused spike. Git is more universally available; JJ may offer better automatic snapshot and conflict semantics. The core contract must not depend on that choice.
Revision boundaries
Git and JJ do not make every filesystem write a useful user-facing revision automatically. The local backend must define when the watcher records a new revision and how rapid edits are grouped.
Possible boundaries include:
after an accepted Basic Memory write;
after a watcher batch settles;
after a short debounce window;
before and after bulk operations;
an explicit user checkpoint.
This is revision capture, not semantic mutation logging: the stored artifact is the complete file/project state represented by the version-control backend.
GET /{project}/notes/{identifier}?revision={ref}
GET /{project}/notes/{identifier}/revisions?cursor=&limit=
GET /{project}/compare?from={ref}&to={ref}&prefix={path}
GET /{project}/compare/file?path=&from=&to=&format=text|semantic
POST /{project}/notes/{identifier}/restore
Exact names are subject to contract review. Responses should use backend-neutral revision terminology even when the underlying provider calls the identifier a version, snapshot, or commit.
search_notes and build_context remain current-index operations in the first version. Adding historical search or a historical knowledge graph would be a separate, substantially larger feature.
Moves, deletes, and identity
Both Tigris and Git naturally organize history by path:
a move may initially appear as one removed path and one added path;
Git rename detection is heuristic;
Tigris object versions belong to object keys;
deleted files may only be discoverable through delete markers or historical tree listings.
The first implementation may offer honest path-based history. Following a note across moves requires a stable note identity that survives historical parsing and should be designed explicitly rather than inferred differently by each backend.
Live updates
A live view of incoming changes can be built later from Tigris object notifications and the local filesystem watcher. That stream may be ephemeral and operational; it does not need to become a second durable content history.
Summary
Add native, backend-aware revision history for Basic Memory notes.
This preserves the useful goals from #1154—visibility into changes, diff, and undo—without recording a second mutation log in the lifecycle-hook inbox from #997.
This is new product work. Basic Memory does not currently have the cross-backend revision abstraction, a local Git/JJ history backend, or revision-aware MCP tools. Existing database versions and checksums are synchronization and optimistic-concurrency state; they are not retained note history.
Why native revisions instead of mutation envelopes
A durable mutation envelope duplicates information already represented by versioned file content and creates another source of truth that must be retained, queried, repaired, and reconciled.
It is also incomplete for a local-first product: an envelope emitted from Basic Memory's write path cannot see edits made directly through Obsidian, VS Code, shell scripts, file synchronization, or other tools. A storage-backed revision source can cover both Basic Memory writes and externally edited files once the local watcher observes them.
The #997 inbox has a different job. It captures selected Claude/Codex lifecycle events with redaction, opt-in configuration, idempotency, and bounded operational concerns. It should not become the authoritative, permanent history of note content.
Revision history should answer:
Actor trust, write authorization, and lifecycle telemetry are separate concerns and are not part of this issue.
Proposed architecture
Define a narrow revision-source capability in core and provide runtime-specific implementations:
The contract should model capabilities, not provider internals. A revision reference is opaque to callers but carries enough scope information for the provider to validate it.
Conceptually, the source needs to support:
The exact Python and public MCP schemas should be reviewed before implementation. The important boundary is that core owns the behavior and response models while cloud/local composition roots provide the storage implementation.
Unsupported capabilities must fail clearly—for example,
revision history is not available for this project—rather than silently returning current content or an empty history.Revision reference semantics
A revision reference may identify:
The response should return the resolved, provider-issued revision identifier so callers can use a stable ref for subsequent reads or comparisons.
Provider-specific identifiers remain opaque:
Timestamp resolution must be explicit and deterministic: each path resolves to its latest stored revision at or before the requested instant.
Cloud implementation: Tigris
Tigris snapshot-enabled buckets already retain historical object content. The cloud implementation should use that history directly instead of creating shadow revision rows or mutation records.
Historical reads
For one note or file:
The current knowledge index describes now. Historical reads must not pretend that the current indexed observations and relations represent the historical graph.
File history
Return a cursor-paginated list containing at least:
Presentation layers may group rapid saves, but storage should preserve the original revisions. Coalescing is a UI concern unless a later retention policy says otherwise.
Recursive compare
A directory or project comparison can be computed cheaply from two at-version listings:
from_revision.to_revision.Example manifest:
Restore
Restoring historical content must read the old revision and write it through the normal current write path. The restoration therefore creates a new head revision and never deletes or rewinds the intervening history.
The write should accept an expected current revision/checksum so a restore cannot silently overwrite a newer concurrent edit. A named snapshot before a bulk restore may provide an additional safety marker, but the underlying object history remains authoritative.
Local implementation: Git or Jujutsu
Local projects need a real revision source rather than emulated empty history.
The implementation must:
Candidate designs
Git object database with a Basic Memory-owned ref
refs/basic-memory/history/<project-id>.Sidecar bare Git repository
Jujutsu-backed history
jjexecutable.The first implementation should be chosen by a focused spike. Git is more universally available; JJ may offer better automatic snapshot and conflict semantics. The core contract must not depend on that choice.
Revision boundaries
Git and JJ do not make every filesystem write a useful user-facing revision automatically. The local backend must define when the watcher records a new revision and how rapid edits are grouped.
Possible boundaries include:
This is revision capture, not semantic mutation logging: the stored artifact is the complete file/project state represented by the version-control backend.
Proposed MCP/API surface
Candidate MCP operations:
Candidate API routes:
Exact names are subject to contract review. Responses should use backend-neutral
revisionterminology even when the underlying provider calls the identifier a version, snapshot, or commit.search_notesandbuild_contextremain current-index operations in the first version. Adding historical search or a historical knowledge graph would be a separate, substantially larger feature.Moves, deletes, and identity
Both Tigris and Git naturally organize history by path:
The first implementation may offer honest path-based history. Following a note across moves requires a stable note identity that survives historical parsing and should be designed explicitly rather than inferred differently by each backend.
Live updates
A live view of incoming changes can be built later from Tigris object notifications and the local filesystem watcher. That stream may be ephemeral and operational; it does not need to become a second durable content history.
Non-goals
build_context.Delivery phases
Core contract and cloud historical reads
read_note,read_content, and file history.Compare
Local backend
Safe restore
Delivery should use small PRs even if this issue remains the public architectural tracker.
Acceptance criteria
build_context, observations, and relations is documented in tool descriptions.Open questions
revisioneverywhere, or retainversionwhere users already recognize it?Related work