RememberStackremember.dev/docs

Keep a source up to date

Specs get edited. The billing migration plan said June; now it says October. If you send the edited file as a new, unrelated document, the memory holds both plans and cannot tell which one the team still stands behind. This page shows how to send the edit as a new version of the same document, and how to tell RememberStack whether the newest version replaces what the old one said.

Setup is in the Quickstart. The model behind this page is in Updating a source: snapshot and living.

Re-send with the same source pair

A document is identified by source_kind + source_ref. Send the edited file with the same pair:

from datetime import UTC, datetime
 
import remember
 
client = remember.Client.from_env()
 
v2 = client.ingest(
    "specs/billing-migration-plan.md",
    source_kind="file",
    source_ref="specs/billing-migration-plan.md",
    source_modified_at=datetime(2026, 9, 17, 14, 0, tzinfo=UTC),
    versioning_mode="living",
    source_version_ref="git:4f2c9e1",
)
print(v2.doc_id, v2.version_id, v2.created)

What happens:

  • Changed bytes create a new version of the same document: the same doc_id, a new version_id, created=True. Wait on the new version_id before you query (Wait until a document is queryable).
  • Identical bytes (the file's latest version already has this content hash) store nothing and return the existing version with created=False. Nothing is reprocessed.
  • Bytes equal to an older version (you reverted an edit) are a new observation and become a new version. The document moves forward; it never silently falls back to an old version.

Only the edited passages are extracted again. Passages whose text and neighbours did not change reuse the claims they already had.

The same with the CLI:

remember ingest specs/billing-migration-plan.md \
  --source-kind file --source-ref specs/billing-migration-plan.md \
  --source-modified-at 2026-09-17T14:00:00+00:00 \
  --versioning-mode living \
  --source-version-ref git:4f2c9e1

Choose snapshot or living

versioning_mode says what an edit means.

ModeUse it forWhat a new version does
snapshot (default)Minutes, reports, dated notes, rolling logs, anything where each version is a record of its moment.Every version stays dated testimony forever. An old version's claims keep counting.
livingSpecs, plans, wikis, a README: documents whose latest version is what the author currently stands behind.The latest version is the document's standing statement. Claims whose passages left it stop counting as current testimony.

A file that drops old lines by itself, such as a log that keeps its last thousand lines, is snapshot even though you re-send the same path. The lines that scroll off were not taken back; in living mode their removal would retract the facts they alone supported.

The mode belongs to the document and is set by its first ingest. Later calls with a different versioning_mode for the same source pair are accepted but do not change it, and neither does a later title. Decide before the first send; if you got it wrong, use a new source_ref.

versioning_mode="living" and source_version_ref require the source pair; without it the client raises ValueError and the API answers 422.

What happens to facts

Facts are what the memory holds true, each backed by claims from one or more documents. When a new version arrives:

Changed statements are new testimony. The October date in version 2 is a new claim. It goes through adjudication like any other: it can supersede the June fact, contradict it, or corroborate something else. The June statement is not deleted; it becomes history with an end date.

In living mode, removal retracts. If a fact's only current support was a passage that is gone from the latest version, the fact is retracted: its invalidated_at is set and the decision is recorded. Its valid_until is not touched, because a removed line says nothing about when the thing stopped being true in the world. It only says the source no longer says it.

If other documents still support the fact, it only loses this document's support; its evidence_count goes down by one and it stays current.

In snapshot mode, nothing is retracted by a new version. Removing a sentence from version 3 does not unsay what version 2 said.

Retraction is visible, never silent. A retracted fact is no longer believed, so the fact operations stop returning it, in every time mode. The SQL view facts_visible_history still shows it with its invalidated_at, and hydrate_relation still returns a retracted relation with its evidence.

A new release is a different problem. When an upgrade re-reads a file you did not change and no longer finds a claim, nothing is retracted: the fact is marked support: "withdrawn" and still returned. Only a change in the source takes a fact back. See Updating a source.

source_modified_at and source_version_ref

  • source_modified_at is when the source last changed, as a timezone-aware UTC datetime. Claims extracted from the version get it as asserted_at. Send the source's own modification time, not the time you uploaded. It is fixed once the version exists; re-sending identical bytes with a different source_modified_at does not change it.
  • source_version_ref is your label for the upstream revision: a git commit, a Drive revision ID, an ETag. It is stored with the version. When you re-send identical bytes with a new source_version_ref, no version is created but the stored label moves to the new value, so a sync job can remember how far it got.

A sync loop for a folder of specs

from datetime import UTC, datetime
from pathlib import Path
 
import remember
 
client = remember.Client.from_env()
root = Path("specs")
new_versions = []
 
for path in sorted(root.glob("*.md")):
    version = client.ingest(
        path,
        source_kind="file",
        source_ref=f"specs/{path.name}",
        source_modified_at=datetime.fromtimestamp(path.stat().st_mtime, tz=UTC),
        versioning_mode="living",
    )
    if version.created:
        new_versions.append(version.version_id)
 
if new_versions:
    client.wait_for_readiness(new_versions)

Run it on a schedule. Unchanged files are no-ops; edited files become new versions; the loop waits only for what changed.

Removing a file

Not sending a file does not remove its document: RememberStack has no signal that the file is gone, and its claims keep counting. When a file leaves the folder, delete its document too. Each document the loop above sent lists its source_ref as source_uri, so one pass over list_documents finds the ones whose file is gone:

from pathlib import Path
 
import remember
 
client = remember.Client.from_env()
root = Path("specs")
present = {f"specs/{path.name}" for path in root.glob("*.md")}
 
cursor = None
while True:
    page = client.list_documents(cursor=cursor)
    for document in page.documents:
        if document.source_kind == "file" and document.source_uri not in present:
            client.delete_document(doc_id=document.doc_id)
    if page.cursor is None:
        break
    cursor = page.cursor

Deleting removes the document's claims from the evidence and closes the facts only it supported; the claims stay as history. If the file comes back later, the sync loop adds it back as a new version. See Deleting a document.

To remove only some passages of a living document, send a version without them instead.

Next