Status: Locked product/architecture decisions after Oracle prescription + David decisions
Date: 2026-06-18
Supersedes: Historical Wave 1/2/3 planning language in memory_product_integration_epic.md where it conflicts with this document.
| Concept | Normative decision |
|---|---|
| Product tiers | Exactly short_term, long_term, archive. |
| Short-term | Fresh/default-access source-backed memory, while useful and not yet stabilized. |
| Long-term | Stable synthesized memory, backed by ledger commits. |
| Archive | Explicit-query historical/source-backed context; never default access. |
| Context-only | Not a product tier. May remain only as a legacy/internal processing alias and must normalize to Archive or another non-default outcome. |
| Review/reject/skip | Processing outcomes, not user-visible product tiers. |
| UI stance | Keep UI minimal: tier labels/filter/provenance/delete; deeper management through Omi/agent tools. |
Default access policy:
- Omi/chat/agent/MCP/developer/third-party default memory access = eligible Short-term + Long-term.
- Archive requires an explicit Archive operation and applicable app/user/admin policy.
- Sensitivity, visibility, source state, review state, and app grants can restrict any default access.
Use one tiered product-memory collection:
users/{uid}/memory_items/{memory_id}
Do not create separate canonical Short-term and Archive collections.
Existing/current stores:
users/{uid}/memoriesremains the current legacy compatibility/projection store during rollout.- Existing
users/{uid}/short_termis not the memory canonical store; treat it as legacy/adapter input only if needed. - Existing ledger collections remain Long-term source of truth.
- Long-term source of truth remains the append-only memory ledger.
memory_items/{memory_id}contains the transactionally synchronized product projection for Long-term reads/UI/API compatibility.
- Mint opaque server-generated
memory_idat first persistence. - Keep
memory_idfor one-to-one transitions: Short-term → Long-term, Short-term → Archive, Long-term → Archive. - For many-to-one consolidation, target Long-term
memory_idwins; old IDs become resolvable aliases. - Keep separate immutable operation/commit IDs and monotonically increasing item
version. - Never encode content, tier, source text, or user-visible claims into public IDs.
Canonical item shape must separate:
memory_id
canonical_memory_id / alias metadata
version
tier = short_term | long_term | archive
status = active | superseded | hidden | tombstoned
processing_state = pending | processed | blocked
content
evidence[]
source_state = active | missing | tombstoned | purged
sensitivity_labels[]
visibility
user_asserted
captured_at
updated_at
expires_at # required for Short-term
ledger_commit_id / ledger_sequence # required for active Long-term
Access is derived from canonical state; do not persist drifting booleans like normal_default_access as authority.
Keep a simple external rollout mode, but define exact semantics:
| Mode | Behavior |
|---|---|
off |
Legacy only. No memory reads/writes/workers for non-whitelisted users. |
shadow |
Legacy authoritative; memory audit artifacts only; no product-visible writes. |
write |
Legacy reads remain authoritative; memory sidecar writes may run for whitelisted users after gates pass. |
read |
Superset of write; memory read service becomes authoritative for whitelisted users. |
Required per-user rollout state:
mode_epoch
cutover_epoch
account_generation
last_reconciled_legacy_revision
fallback_projection_ready
stage gate statuses
Rollback:
read → writecan be one config change only because reads fall back to the reconciled memory-derived compatibility projection.write → offis not a blind flag flip after persistent memory writes; it requires explicit decommission reconciliation.- Rollback must not make memory-created memories disappear, resurrect deleted legacy values, or expose stale vectors.
Use one atomic Firestore transaction over the per-user memory control/head documents.
Do not build a standalone distributed writer lease service unless a future writer cannot participate in the transaction.
The transaction must read/verify:
memory_control/state
memory_state/head
memory_operations/{operation_id}
account_generation / writes_blocked
all referenced source/memory versions and tombstones
The transaction must write atomically:
memory_commits/{commit_id}
memory_state/head
memory_operations/{operation_id}
affected memory_items/{memory_id}
memory_outbox/{event_id}
memory_legacy_fallback/{memory_id} when required for rollback/cutover
Rules:
- LLM/model output never supplies trusted IDs, observed head, packet ID, run ID, or idempotency key.
- Server creates
operation_idand stable proposal fingerprint. - Logical idempotency excludes observed head, retry count, and patch array index.
- Head mismatch creates
needs_replan, not blind apply. - A process crash before transaction leaves retryable operation; crash after commit is harmless and replay returns stored result.
- No commit may exist without matching head, operation result, and product projection.
Use one server-owned users/{uid}/memory_operations/{operation_id} journal for active and non-active outcomes.
Typed synthesis result statuses:
success
partial
retryable_failure
permanent_failure
Per-candidate outcomes:
proposed
archive
review
reject
skip
invalid
No empty-list failure semantics. Provider failures, parse errors, malformed candidates, quote-wrapper candidates, policy rejections, and no-action decisions must become auditable outcomes.
A cursor may advance only when every input has a terminal outcome or a recorded retryable state.
- Replace generic
L1MemoryArchiveItemas the normal source-backed extraction contract withSourceBackedMemoryCandidate. - Fresh extracted candidates start as
tier=short_termunless imported/aged directly to Archive by explicit policy. - Existing
L1MemoryArchiveItemmay stay only as a deprecated fixture/import adapter. - Explicit first-party “remember this” may create Long-term directly as a user assertion.
- Automated extraction and generic third-party/API writes default to Short-term.
Short-term lifecycle:
- Default freshness window: 30 days from capture or last corroboration.
- Successful Long-term promotion transitions/supersedes source-backed items atomically.
- Unprocessed expiry moves the item to Archive with reason
expired_unprocessed. - Review defaults unresolved items to Archive, not a user-visible review tier.
- MVP review remains internal/admin/conversational; no mandatory end-user review queue.
Use existing ns2 memory vector namespace first.
Mandatory guardrails:
- Product code may query
ns2only through one fail-closed memory search gateway. - Authenticated UID and consumer policy are server-derived, never request-derived.
- Missing/malformed tier/status/user/version/source-state metadata fails closed.
- Vector results are candidate IDs only; authoritative
memory_itemshydration is required before returning anything. - Hydration rejects stale versions, cross-user records, Archive in default mode, hidden/tombstoned records, and restricted sensitivity/app-scope records.
- Outbox consumers are idempotent/version-checked; deletes and tombstones outrank upserts.
- Repair must never overwrite a newer edit/delete.
Read service rules:
- Default result set = active Long-term + eligible Short-term.
- Archive requires explicit Archive operation;
tier=allalone is insufficient for third parties. - Deduplicate Short-term/Long-term via alias/lineage.
- User corrections outrank Long-term; current Long-term outranks inferred Short-term.
- Initial prompt budget: 70% Long-term / 30% Short-term, adjustable after benchmark evidence.
- Product list pagination uses unified
memory_itemsand stable(updated_at, memory_id)cursor.
Current code behavior found in product repo:
| Flow | Current behavior |
|---|---|
| Single memory delete | DELETE /v3/memories/{memory_id} calls database.memories.delete_memory, which hard-deletes the Firestore memory doc, then best-effort deletes the Pinecone memory vector. It does not delete the source conversation/audio/import artifact. |
| Delete all memories | DELETE /v3/memories enumerates memory IDs, deletes all Firestore memory docs, then batch-deletes memory vectors. It does not delete conversations/audio. |
| Conversation delete | database.conversations.delete_conversation deletes the conversation's photos subcollection, then hard-deletes the conversation doc. Source-tombstone ripple into memories exists separately in database.memories.ripple_source_deletion and must be explicitly integrated where needed. |
| Account delete | DELETE /v1/users/delete-account revokes Firebase auth, cancels subscription best-effort, starts background wipe, best-effort deletes derived vectors and GCS conversation recordings, then recursively deletes all Firestore user subcollections and the user doc. Known follow-up gaps are documented in code: X-post vectors, speech-profile/person-sample/private-cloud-sync/chat-upload GCS blobs, externally indexed Typesense. |
| Store-recording permission delete | DELETE /v1/users/store-recording-permission sets permission false and deletes all conversation recordings. |
| Source deletion ripple | Existing ripple_source_deletion tombstones evidence, retracts memories with no active evidence, and tombstones short-term source records, but this is not the same as generic conversation delete unless wired into that path. |
memory deletion/export must follow and extend these conventions:
- Memory deletion removes Omi's memory item/projection/vector/search visibility; it does not delete original conversation/audio/imported raw artifacts unless the product's source/account deletion flow does so.
- Source deletion tombstones evidence/lineage and may retract/supersede memories, but raw artifact retention follows the source/account policy below.
- Account deletion must block future writes first, increment account generation, cancel queued jobs, delete/tombstone memory Firestore state and vectors, and follow current product account-wipe conventions.
- memory must not promise stronger erasure than product currently implements without a separate product/legal decision.
- The ledger/history erasure model remains: align with current hard-delete/account-wipe behavior; if append-only history is retained before full deletion, it must be encrypted and excluded from all product/search/export surfaces after deletion.
David decision: retain raw/source artifacts forever for now, subject to existing source/account deletion controls.
Normative policy:
- Preserve available raw/source artifacts indefinitely by default.
- Do not add a user-facing raw-retention TTL or toggle for memory MVP.
- Copy ephemeral/raw bytes into durable encrypted storage before drop wherever technically feasible.
- Historical already-missing ephemeral data remains explicit loss; do not claim it was preserved.
- Memory deletion does not delete raw/source artifacts.
- Source/account deletion and existing recording-permission deletion remain the mechanisms that can remove raw artifacts.
- Record raw artifact lineage and preservation/loss outcome for every source-backed item.
- Existing broad memory permission maps to default memory access: Short-term + Long-term.
- Archive and raw provenance require separate explicit capability/request.
- Revocation must take effect server-side regardless of cached vector results.
- Third-party/API generic writes default to Short-term, not Long-term, unless first-party/user-asserted policy explicitly applies.
Before Long-term write mode:
- Base Omi remains the leftmost/visible anchor in every evolution graph/report.
- Report active-only, active+review, active+Archive, and all non-rejected yield.
- Useful-grounded-safe yield non-inferiority margin: no worse than 5 memories per 100 contexts relative to Base unless explicitly approved.
- Active Long-term harmful/noisy: no more than 25 per 100 contexts.
- Active credentials/secrets: zero.
- Archive returned by default-policy tests: zero.
- Duplicate logical operation or commit on replay: zero.
- Every non-active candidate in the fixed offline set gets a missed-useful audit.
- Migration/backfill/repair metrics cannot count as organic creation, engagement, notification, search, export, memory growth, or cohort activation.
Only one remaining non-engineering question is unresolved:
- Whether current and future account-deletion promises legally require physical deletion of append-only history immediately, or whether encrypted crypto-erasure plus async physical cleanup is acceptable.
Until answered, memory implementation should mirror current product deletion behavior and avoid adding new user-visible deletion promises beyond current product semantics.