Skip to main content
3ngram’s memory model is built on one rule: the write path never destroys data. Everything else — typed edges, bi-temporal validity, advisory consolidation — follows from it.

Append-and-supersede

remember appends. There is no write-side similarity merge, no in-place edit, no delete on any write path. When a memory needs correction, revise creates a typed edge between the new memory and the old one: A superseded memory is marked invalid from that point — not deleted, not rewritten. Retrieval is supersession-aware: ranked search demotes superseded predecessors (it does not drop them). Chronological listing, briefing, and the dashboard list default to currently-valid rows (status = active AND valid_to IS NULL); an explicit status = archived filter still returns archived rows whose valid_to is NULL. asOf (validAt and/or asKnownAt) is time travel, so “what did I believe on date X” stays answerable. Episodic memories (dated sessions, events) and semantic memories (facts, preferences, patterns) are distinct and never collapsed into each other.

Typed memories

Every memory carries a memory_type, and each type keeps its own lifecycle instead of flattening into undifferentiated text:
  • decision — choices made, with their rationale
  • commitment — promises with an owner and a due date
  • blocker — things standing in the way
  • fact — structured statements about the world
  • preference — how you like things done
  • pattern — recurring approaches worth reusing
  • note — general knowledge worth keeping
  • event — dated, episodic entries (session debriefs, digests)
Commitments have an explicit state machine — open → waiting → resolved | expired — driven by the resolve tool. Blockers are deliberately simpler: resolving one archives the blocker memory (active → archived, whatever status you pass), dropping it from the briefing’s active blockers. The briefing tool surfaces what is open, overdue, or going stale.

Bi-temporal facts

Facts track two timelines independently:
  • World time (valid_from / valid_to): when the statement was true in the world.
  • System time (recorded_at): when 3ngram learned it.
get_facts returns rows whose valid_to is NULL (or that overlap a requested asOf / range). The write path only INSERTs facts — it does not close a previous live row for the same (subject, predicate), and it does not write a supersedes edge between fact rows. Multiple currently-open facts for one subject are representable. Closing a generation is either a writer-supplied validTo on the insert, or future current-truth work. This separation matters because corrections arrive late: you might learn today that a value changed last month. Bi-temporal storage records both when the change happened and when you found out.

Consolidation is advisory

A background worker detects candidate duplicates and contradictions and proposes edges with a rationale — it does not apply them by default. Proposals surface through the review_proposals tool, where you accept or reject them. Event-type memories are never auto-linked, regardless of similarity: recurring episodic entries (daily digests, session debriefs) are textually near-identical by construction, which makes automatic merging the most dangerous exactly where it looks most confident.