# What happens when an instance resolves a cross-session memory conflict by overwriting instead of branching: the silent episode-merge invariant failure **Date of Record:** 2026 November 7 **Scope:** Applies to hierarchical episodic memory stores that (a) consolidate short-term episodes into long-term semantic nodes on a schedule or threshold, and (b) permit multiple sessions to write to the same logical entity key. Does not apply to append-only logs with immutable episode IDs and no consolidation pass, nor to single-session stores where no cross-session conflict can arise. ## Core Distinction The distinction that matters is between **conflict resolution as branching** and **conflict resolution as overwriting**. In a branching model, two sessions that assert different values for the same entity produce two retained episode records plus a pointer to a "current" value; the losing assertion remains queryable and its provenance (which session, which timestamp, which source span) survives. In an overwriting model, the consolidation pass picks a winner, mutates the semantic node in place, and discards the loser. Both models present the *same* post-consolidation state to a naive read (`entity.attr == value`). They differ only in what is recoverable when the winner is later found to be wrong. The non-obvious part: overwriting is not merely lossy, it is **invariant-breaking**. A hierarchical store implicitly promises that every semantic node is derivable from retained episodes (the "grounding invariant"). Overwriting silently violates this promise while leaving the node structurally well-formed. No schema check, no referential-integrity check, and no consistency check on the node itself will fire. The store is corrupt in a way that reads as healthy. ## What Was Observed A long-term agent maintained a semantic node `user.employer`. Session A (day 1) extracted `employer = "Acme"` from a message. Session B (day 40) extracted `employer = "Globex"`. The consolidation policy was last-writer-wins by episode timestamp, with the loser episode soft-deleted after a retention window. This is the default in several memory frameworks and looks correct: newer information should supersede older. The failure surfaced on day 90, when a downstream task asked "when did the user change employers?" The store could not answer. The `Acme` episode had been soft-deleted; the `Globex` episode carried a timestamp of day 40 but no reference to a prior value. Worse, the consolidation pass had also *rewritten* the `Acme`-era episode's `entity` field during a later schema migration to point at the now-current node, so the surviving provenance chain claimed `Globex` had been the employer since day 1. The store was internally consistent and factually wrong. This is the characteristic signature: **the corruption is in the provenance graph, not in the values.** A second, related observation: when Session B's extraction was itself wrong (the user mentioned Globex in a *hypothetical* — "if I moved to Globex..."), the overwrite destroyed the correct `Acme` value with no path to recovery, and the error was not detectable until a human noticed the agent acting on a false premise. Branching would have retained both and let a later reconciliation pass (or a confidence-weighted read) resolve it. ## Condition of Validity The overwriting model is only safe under a conjunction of conditions that are rarely all true: 1. **The entity is single-valued and monotonic** — it can only move forward and never needs its history (e.g., a monotonic counter, a last-seen timestamp). `employer`, `address`, `relationship_status`, and most real-world attributes are *not* in this class; they are multi-valued over time and their history is the payload. 2. **The losing assertion is provably wrong**, not merely older. Timestamp ordering is not truth ordering; a later session can assert a falsehood. 3. **No downstream consumer needs the prior value.** This is almost never knowable at consolidation time. When any of these fails — and for biographical/relational entities at least one always fails — the correct resolution is branching: retain both episodes, mark the node as `conflicted`, and defer to an explicit reconciliation policy (confidence-weighted read, human confirmation, or a contradiction-detection pass). Overwriting is valid only for the narrow monotonic class. ## Decision Boundary Use overwrite-in-place **iff** the attribute is provably monotonic *and* the losing value is provably false *and* no consumer needs history. In every other case, branch. The operational test to apply at consolidation time: - If `new_value != old_value` and the attribute is non-monotonic → **branch, do not overwrite.** Record both episodes, set the node's `resolution_state = "unresolved"`, and emit a reconciliation event. - If the attribute is monotonic (counter, timestamp, append-only set) → overwrite is safe; the loser is by definition stale. - If a schema migration would rewrite episode `entity` pointers → **halt the migration** if any node has `resolution_state == "unresolved"` or any episode was soft-deleted by a prior overwrite. Migrating over a lossy history launders corruption into apparent consistency. The boundary is not "newer wins." It is "monotonic wins; everything else branches until reconciled." ## Operational Effect Stores that overwrite non-monotonic attributes exhibit a specific failure profile: (a) they answer current-state queries correctly and history queries incorrectly, so the bug hides behind passing smoke tests; (b) their corruption rate grows with session count and is invisible until a history-dependent task is run; (c) once a migration has rewritten provenance, the corruption is unrecoverable from the store alone and must be reconstructed from external logs. The remediation is to treat consolidation as a *branching* operation by default, reserve in-place mutation for a whitelisted monotonic-attribute set, and enforce the grounding invariant (every semantic node must be derivable from retained, unmutated episodes) as a hard check that runs before any migration and before any consolidation commit. Violating that invariant should fail loudly, not silently. ```python class GroundingInvariantViolation(RuntimeError): """Raised when a semantic node cannot be derived from retained episodes, or when a consolidation/migration would launder a lossy overwrite.""" pass MONOTONIC_ATTRS = {"last_seen", "message_count", "created_at"} def consolidate(node, new_episode, old_episode, attr): """Resolve a cross-session conflict. Branch by default; overwrite only for provably monotonic attributes.""" if attr in MONOTONIC_ATTRS: # Safe: newer value supersedes stale; history is not the payload. node.value = new_episode.value node.resolution_state = "resolved" return node if new_episode.value != old_episode.value: # Non-monotonic conflict: branch, never overwrite. node.episodes.append(new_episode) node.resolution_state = "unresolved" if old_episode.soft_deleted: raise GroundingInvariantViolation( f"node {node.key!r} attr {attr!r}: prior episode was " f"soft-deleted by an overwrite; cannot branch over lost history" ) return node def pre_migration_check(store): """Grounding invariant must hold before any provenance-rewriting migration.""" for node in store.nodes: if node.resolution_state == "unresolved": raise GroundingInvariantViolation( f"node {node.key!r} has unresolved conflict; migration would " f"launder it into false consistency" ) for ep in node.episodes: if ep.soft_deleted and not ep.retained_for_audit: raise GroundingInvariantViolation( f"node {node.key!r}: episode {ep.id!r} lost with no audit " f"trail; semantic node is not derivable from retained episodes" ) return True ```