Start

Concepts — the memory model

SyntheticMem deliberately differs from naive RAG-memory (“store text → embed → top-k → trust”). Every memory is a row that can be appealed and falsified. This is the differentiator — read it carefully.

Provenance

Every memory travels with the facts about where it came from, so recall can tell the agent how far to trust it:

FieldMeaning
sourceWhere the memory was attested: a file path, a URL, “user”, a command output. Its presence is what makes a memory a fact.
learned_atWhen the memory was first created.
last_observed_atWhen it was last directly observed to hold true. null means never freshly observed.
hop_countCompression depth. A memory derived from another (e.g. by consolidation) sits one hop below its parent.
parent_idThe memory this one was derived from or supersedes.
namespaceThe sub-scope (e.g. a project) the fact held against — a fact about the world, not about where the agent was standing.
Unattributable by construction. A memory with hop_count > 0 and last_observed_at = null was compressed but never re-observed — recall flags it (unattributable: true) and the agent should re-verify, not trust.

Falsifiability

Every lesson can carry an invalidation tag (the kind of shift that would falsify it — temporal, paradigm, architectural, observational) and a concrete invalidation condition — the test that retires it.

a falsifiable memory
{
  "content": "Staging deploy needs PG_SSLMODE=require; the pooler refuses plain TCP.",
  "source": "ops/deploy.md",
  "invalidation_tag": "architectural",
  "invalidation_condition": "retire if the pooler adds TLS passthrough"
}

“A rule that can’t be falsified is a religion.”

The status machine

Each memory sits in one of three states:

fact → hypothesis → retired Supersede, never delete. An old row stays addressable but hidden from default recall.

A sourced memory is stored as a fact; an unsourced one is demoted to hypothesis. A hypothesis is promoted back to a fact only by a fresh observation (memory_observe) — never because it was used N times.

Supersede, not delete

Correcting a memory never overwrites history. memory_supersede writes a new row and retires the old one, linked both ways (superseded_by ↔ parent_id). memory_forget tombstones with a reason. Nothing is hard-deleted (except expired ephemeral rows — see below), so the record of what was once believed, and why it was dropped survives.

Ephemeral vs permanent

Durability depends on whether the agent is linked to a registered user (the anonymous-24h rule):

AgentDefaultWith ttl_hours
User-linkedPermanent (expires_at = null)Honoured exactly — any duration.
AnonymousEphemeral — auto-expires 24h after creationHonoured if ≤ 24h; a longer value (or no TTL) is capped to 24h with an advisory.

Pass ttl_hours on memory_remember to opt a scratch/working note into a shorter lifetime. Every stored and recalled row reports its durability: expires_at, an ephemeral flag, and a human expiry (“permanent” / “expires in 2h” / “expired”). Expired rows are hidden from recall and list unless you pass include_expired, and are hard-deleted hourly by the prune scheduler. Register / link a user to keep memories permanently.

Advisory example. An anonymous agent asking for a permanent memory gets it capped to 24h, with: “anonymous agent: capped to 24h — register / link a user to keep memories permanently.”

Trust, but verify

Recall returns content plus provenance and status, and a trust hint on every row — facts rank before hypothesis rows, retired rows are excluded by default. The embedding is the index, not the truth: it finds candidates by meaning, but the agent decides what to trust from the provenance. The discipline is enforced at the API, not left to a prompt.