Agentic Notes Library

Chapter 12: Memory Write Policies, Validation, and Governance

Durable memory in agentic systems is not a passive data store—it is a live epistemic substrate upon which all future reasoning, retrieval, planning, and tool invocation depend. An unchecked write to memory is architecturally equivalent t...

March 20, 2026 24 min read 5,171 words
Chapter 12MathRaw HTML

Preamble#

Durable memory in agentic systems is not a passive data store—it is a live epistemic substrate upon which all future reasoning, retrieval, planning, and tool invocation depend. An unchecked write to memory is architecturally equivalent to an unreviewed commit to a production codebase: it silently alters every downstream decision surface. This chapter defines the complete engineering discipline required to govern what enters memory, how it is validated, how it is versioned, how it is shared, how it decays, and how it is audited—treating memory as a typed, governed, observable infrastructure layer with explicit write policies, mechanical consistency guarantees, and continuous quality measurement.

The central thesis is precise: the write path is the security boundary of agent cognition. Every concept in this chapter—gated admission, validation pipelines, deduplication, provenance, versioning, human approval, garbage collection, access control, consistency models, regulatory compliance, anti-patterns, and quality metrics—exists to enforce that boundary with production-grade rigor.


12.1 Write-Path Architecture: Gated Admission to Durable Memory#

12.1.1 Foundational Principle#

Memory admission in an agentic system must be treated as a gated, transactional pipeline rather than an append operation. The write path is the single point at which ephemeral agent-generated content is promoted to persistent epistemic state. Every item that passes through this gate alters the agent's future reasoning distribution. Therefore, the write path must enforce:

  • Schema conformance before structural admission
  • Factual verification before semantic admission
  • Contradiction detection before integration with existing knowledge
  • Deduplication before storage allocation
  • Provenance attachment before any item becomes citable
  • Authorization and approval gating before high-stakes writes commit

12.1.2 Write-Path Topology#

The write path is decomposed into a staged pipeline with explicit checkpoints:

┌─────────────┐     ┌──────────────┐     ┌───────────────┐     ┌─────────────┐
│  Write       │────▶│  Validation  │────▶│  Dedup /      │────▶│  Provenance │
│  Request     │     │  Gate        │     │  Merge Gate   │     │  Stamping   │
└─────────────┘     └──────────────┘     └───────────────┘     └─────────────┘

                    ┌──────────────┐     ┌───────────────┐            ▼
                    │  Durable     │◀────│  Approval /   │◀───┌─────────────┐
                    │  Commit      │     │  Policy Gate  │    │  Version    │
                    └──────────────┘     └───────────────┘    │  Assignment │
                                                              └─────────────┘

Each gate is a typed, independently deployable service exposing a gRPC interface with explicit accept/reject/defer semantics.

12.1.3 Formal Write Request Schema#

Every memory write request is a typed message conforming to a versioned Protobuf contract:

MemoryWriteRequest {
  request_id        : UUID           // Idempotency key
  source_agent_id   : AgentID        // Originating agent
  target_layer      : MemoryLayer    // {WORKING, SESSION, EPISODIC, SEMANTIC, PROCEDURAL}
  content           : MemoryItem     // Typed payload
  evidence_refs     : List<EvidenceRef>  // Provenance chain
  confidence        : float64        // ∈ [0.0, 1.0]
  write_policy      : WritePolicyID  // Policy governing admission
  ttl               : Duration       // Requested time-to-live
  idempotency_token : bytes          // Content-derived hash for replay safety
  deadline          : Timestamp      // Request timeout
}

12.1.4 Memory Layer Targeting#

Not all writes target the same durability tier. The write request must explicitly declare the target memory layer, each with distinct admission criteria:

Memory LayerDurabilityAdmission StrictnessTypical TTLWrite Authority
WorkingEphemeralNone (free writes)MinutesAny agent
SessionRequest-scopedSchema check onlyHoursSession-owning agent
EpisodicDurableFull validation pipelineDays–monthsValidated agents
SemanticCanonicalFull pipeline + human approvalIndefiniteGovernance-approved
ProceduralCanonicalFull pipeline + CI test gateIndefiniteGovernance-approved

12.1.5 Gating Decision Function#

The admission decision at each gate is modeled as a function:

Gi:(MemoryWriteRequest,GateStatei){ACCEPT,REJECT(r),DEFER(t)}G_i : (\text{MemoryWriteRequest}, \text{GateState}_i) \rightarrow \{\texttt{ACCEPT}, \texttt{REJECT}(r), \texttt{DEFER}(t)\}

where rr is a structured rejection reason and tt is a deferral deadline. The composite write-path decision is:

Commit(w)=i=1N[Gi(w,si)=ACCEPT]\text{Commit}(w) = \bigwedge_{i=1}^{N} \left[ G_i(w, s_i) = \texttt{ACCEPT} \right]

A write commits if and only if every gate in the pipeline returns ACCEPT\texttt{ACCEPT}. Any single REJECT\texttt{REJECT} aborts the transaction. Any DEFER\texttt{DEFER} suspends the item in a pending queue with bounded retry.

12.1.6 Pseudo-Algorithm: Write-Path Execution#

ALGORITHM WritePathExecute(request: MemoryWriteRequest) → WriteResult
 
  // Step 1: Idempotency check
  IF idempotency_store.contains(request.idempotency_token) THEN
    RETURN WriteResult(status=ALREADY_COMMITTED, ref=idempotency_store.get(request.idempotency_token))
  END IF
 
  // Step 2: Deadline enforcement
  IF now() > request.deadline THEN
    RETURN WriteResult(status=DEADLINE_EXCEEDED)
  END IF
 
  // Step 3: Sequential gate traversal
  gates ← [SchemaGate, FactualVerificationGate, ContradictionGate,
            DeduplicationGate, ProvenanceGate, PolicyApprovalGate]
 
  FOR EACH gate IN gates DO
    result ← gate.evaluate(request, gate.state())
    IF result = REJECT(reason) THEN
      emit_metric("memory.write.rejected", {gate: gate.name, reason: reason})
      RETURN WriteResult(status=REJECTED, reason=reason, gate=gate.name)
    ELSE IF result = DEFER(deadline) THEN
      pending_queue.enqueue(request, retry_deadline=deadline)
      RETURN WriteResult(status=DEFERRED, retry_after=deadline)
    END IF
  END FOR
 
  // Step 4: Version assignment
  version ← version_log.next_version(request.target_layer)
  stamped_item ← attach_version(request.content, version)
 
  // Step 5: Durable commit (atomic)
  commit_ref ← durable_store.commit(stamped_item, request.target_layer)
 
  // Step 6: Idempotency registration
  idempotency_store.put(request.idempotency_token, commit_ref, ttl=24h)
 
  // Step 7: Observability
  emit_metric("memory.write.committed", {layer: request.target_layer, agent: request.source_agent_id})
  emit_trace("memory.write", request.request_id, commit_ref)
 
  RETURN WriteResult(status=COMMITTED, ref=commit_ref, version=version)
END ALGORITHM

12.1.7 Idempotency Guarantees#

The idempotency token is computed as a deterministic hash of the write content and source context:

idempotency_token(w)=SHA-256 ⁣(canonical_serialize(w.content)    w.source_agent_id    w.target_layer)\text{idempotency\_token}(w) = \text{SHA-256}\!\left(\text{canonical\_serialize}(w.\text{content}) \;\|\; w.\text{source\_agent\_id} \;\|\; w.\text{target\_layer}\right)

This ensures that retried writes—due to network failures, timeout-induced retransmissions, or agent loop replays—are absorbed without duplication, a critical invariant for bounded-loop agents that may re-execute the same reasoning step.

12.1.8 Backpressure and Rate Limiting#

The write path enforces per-agent and per-layer rate limits to prevent memory flooding:

rate(a,l,Δt)={ww.source_agent_id=aw.target_layer=lw.timestamp[tΔt,t]}Δt\text{rate}(a, l, \Delta t) = \frac{|\{w \mid w.\text{source\_agent\_id} = a \wedge w.\text{target\_layer} = l \wedge w.\text{timestamp} \in [t - \Delta t, t]\}|}{\Delta t}

If rate(a,l,Δt)>limit(a,l)\text{rate}(a, l, \Delta t) > \text{limit}(a, l), subsequent writes receive DEFER\texttt{DEFER} with exponential backoff. This mechanically prevents runaway agent loops from saturating durable memory.


12.2 Validation Pipeline: Schema Conformance, Factual Verification, Contradiction Detection#

12.2.1 Pipeline Architecture#

The validation pipeline is the epistemic firewall of the memory system. It consists of three sequentially applied validation stages, each of which must independently pass before the write advances:

  1. Schema Conformance Gate — structural correctness
  2. Factual Verification Gate — empirical grounding
  3. Contradiction Detection Gate — consistency with existing knowledge

12.2.2 Stage 1: Schema Conformance#

Every memory item must conform to a typed schema registered in a schema registry. The schema defines:

  • Required and optional fields with type constraints
  • Value domain restrictions (enumerations, ranges, format patterns)
  • Relationship cardinality constraints
  • Embedding dimensionality requirements (if vector-indexed)

Formal Definition. Let S\mathcal{S} be the schema for memory layer ll, and let mm be the candidate memory item. Schema conformance is:

SchemaValid(m,S)=fS.required[ffields(m)type(m.f)S.type(f)]cS.constraintsc(m)\text{SchemaValid}(m, \mathcal{S}) = \bigwedge_{f \in \mathcal{S}.\text{required}} \left[ f \in \text{fields}(m) \wedge \text{type}(m.f) \preceq \mathcal{S}.\text{type}(f) \right] \wedge \bigwedge_{c \in \mathcal{S}.\text{constraints}} c(m)

where \preceq denotes type compatibility (subtype relation) and c(m)c(m) denotes satisfaction of constraint cc applied to item mm.

Pseudo-Algorithm: Schema Validation

ALGORITHM SchemaValidate(item: MemoryItem, schema: MemorySchema) → ValidationResult
 
  errors ← []
 
  // Required field presence and type check
  FOR EACH field IN schema.required_fields DO
    IF field.name ∉ item.fields THEN
      errors.append(MissingRequiredField(field.name))
    ELSE IF NOT type_compatible(item.fields[field.name].type, field.declared_type) THEN
      errors.append(TypeMismatch(field.name, expected=field.declared_type,
                                  actual=item.fields[field.name].type))
    END IF
  END FOR
 
  // Domain constraints
  FOR EACH constraint IN schema.constraints DO
    IF NOT constraint.evaluate(item) THEN
      errors.append(ConstraintViolation(constraint.id, constraint.description))
    END IF
  END FOR
 
  // Embedding dimensionality (if applicable)
  IF schema.requires_embedding AND item.embedding IS NOT NULL THEN
    IF dim(item.embedding) ≠ schema.embedding_dim THEN
      errors.append(EmbeddingDimMismatch(expected=schema.embedding_dim, actual=dim(item.embedding)))
    END IF
  END IF
 
  IF errors IS EMPTY THEN
    RETURN ValidationResult(status=ACCEPT)
  ELSE
    RETURN ValidationResult(status=REJECT, reasons=errors)
  END IF
END ALGORITHM

12.2.3 Stage 2: Factual Verification#

Factual verification assesses whether the content of the memory item is empirically grounded against authoritative sources. This stage is especially critical for semantic and procedural memory layers, where incorrect facts permanently bias downstream reasoning.

Verification Strategy Taxonomy:

StrategyMechanismLatencyApplicability
Source cross-referenceCompare claim against cited evidenceLowItems with evidence_refs
Retrieval-augmented checkQuery external knowledge basesMediumFactual assertions
Multi-model consensusSubmit claim to kk independent modelsHighHigh-stakes writes
Temporal validity checkVerify claim not stale/supersededLowTime-sensitive facts
Code execution verificationExecute code-derived claims in sandboxMediumProcedural memory

Formal Scoring. The factual verification score for a memory item mm with evidence set E={e1,,ek}E = \{e_1, \ldots, e_k\} is computed as:

Vfact(m)=1Ei=1Eαisupport(m,ei)authority(ei)freshness(ei)V_{\text{fact}}(m) = \frac{1}{|E|} \sum_{i=1}^{|E|} \alpha_i \cdot \text{support}(m, e_i) \cdot \text{authority}(e_i) \cdot \text{freshness}(e_i)

where:

  • support(m,ei)[0,1]\text{support}(m, e_i) \in [0, 1] measures semantic entailment from evidence eie_i to claim mm
  • authority(ei)[0,1]\text{authority}(e_i) \in [0, 1] is the source authority weight (e.g., official documentation 1.0\rightarrow 1.0, LLM-generated 0.3\rightarrow 0.3)
  • freshness(ei)=exp ⁣(λ(tnowtei))\text{freshness}(e_i) = \exp\!\left(-\lambda \cdot (t_{\text{now}} - t_{e_i})\right) applies exponential freshness decay with rate λ\lambda
  • αi\alpha_i normalizes weights such that αi=E\sum \alpha_i = |E| (uniform by default, adjustable per evidence class)

The item passes factual verification if Vfact(m)τfact(l)V_{\text{fact}}(m) \geq \tau_{\text{fact}}(l), where τfact(l)\tau_{\text{fact}}(l) is the layer-specific threshold:

τfact(EPISODIC)=0.6,τfact(SEMANTIC)=0.8,τfact(PROCEDURAL)=0.9\tau_{\text{fact}}(\text{EPISODIC}) = 0.6, \quad \tau_{\text{fact}}(\text{SEMANTIC}) = 0.8, \quad \tau_{\text{fact}}(\text{PROCEDURAL}) = 0.9

12.2.4 Stage 3: Contradiction Detection#

Contradiction detection ensures that a new memory item does not logically or semantically conflict with existing validated memory. This is the most computationally expensive validation stage but is essential to prevent epistemic corruption.

Contradiction Detection Methods:

  1. Exact negation detection — syntactic pattern matching for explicit negations of existing facts
  2. Semantic entailment contradiction — NLI (Natural Language Inference) model classifying the relationship between the new item and existing items as {entailment,neutral,contradiction}\{\text{entailment}, \text{neutral}, \text{contradiction}\}
  3. Constraint-graph violation — checking whether the new item violates any integrity constraints in a structured knowledge graph
  4. Temporal inconsistency — detecting claims that contradict the known temporal ordering of events

Formal Definition. Let Ml\mathcal{M}_l denote the existing memory corpus for layer ll, and let mnewm_{\text{new}} be the candidate item. Define the contradiction set:

C(mnew,Ml)={mjMl  |  NLI(mj,mnew)=CONTRADICTIONsim(mj,mnew)>σtopic}\mathcal{C}(m_{\text{new}}, \mathcal{M}_l) = \left\{ m_j \in \mathcal{M}_l \;\middle|\; \text{NLI}(m_j, m_{\text{new}}) = \texttt{CONTRADICTION} \wedge \text{sim}(m_j, m_{\text{new}}) > \sigma_{\text{topic}} \right\}

where sim(mj,mnew)\text{sim}(m_j, m_{\text{new}}) is the cosine similarity of their embeddings and σtopic\sigma_{\text{topic}} is a topic-relevance threshold (typically σtopic0.5\sigma_{\text{topic}} \geq 0.5) ensuring we only flag contradictions within the same semantic neighborhood, not between unrelated facts.

Decision Logic:

  • If C=0|\mathcal{C}| = 0: ACCEPT\texttt{ACCEPT} (no contradiction)
  • If C>0|\mathcal{C}| > 0 and mnew.confidence>maxmjCmj.confidencem_{\text{new}}.\text{confidence} > \max_{m_j \in \mathcal{C}} m_j.\text{confidence}: DEFER\texttt{DEFER} to human review with supersession proposal
  • If C>0|\mathcal{C}| > 0 and mnew.confidencemaxmjCmj.confidencem_{\text{new}}.\text{confidence} \leq \max_{m_j \in \mathcal{C}} m_j.\text{confidence}: REJECT\texttt{REJECT} with contradiction report

Pseudo-Algorithm: Contradiction Detection

ALGORITHM ContradictionDetect(m_new: MemoryItem, layer: MemoryLayer) → ValidationResult
 
  // Step 1: Retrieve semantically proximate items
  candidates ← semantic_index.query(m_new.embedding, top_k=50, layer=layer)
  candidates ← FILTER(candidates, λ c: cosine_sim(c.embedding, m_new.embedding) > σ_topic)
 
  contradictions ← []
 
  // Step 2: NLI classification
  FOR EACH candidate IN candidates DO
    nli_result ← nli_model.classify(premise=candidate.text, hypothesis=m_new.text)
    IF nli_result.label = CONTRADICTION AND nli_result.score > 0.85 THEN
      contradictions.append(ContradictionRecord(
        existing_item=candidate,
        nli_score=nli_result.score,
        topic_similarity=cosine_sim(candidate.embedding, m_new.embedding)
      ))
    END IF
  END FOR
 
  // Step 3: Decision
  IF contradictions IS EMPTY THEN
    RETURN ValidationResult(status=ACCEPT)
  ELSE
    max_existing_confidence ← MAX(c.existing_item.confidence FOR c IN contradictions)
    IF m_new.confidence > max_existing_confidence THEN
      RETURN ValidationResult(status=DEFER,
        reason="Contradicts existing items but has higher confidence; requires human review",
        contradictions=contradictions,
        proposed_action=SUPERSEDE)
    ELSE
      RETURN ValidationResult(status=REJECT,
        reason="Contradicts existing higher-confidence items",
        contradictions=contradictions)
    END IF
  END IF
END ALGORITHM

12.2.5 Composite Validation Score#

For observability and audit purposes, every write request that passes through the validation pipeline receives a composite validation score:

Vcomposite(m)=ws1[SchemaValid(m)]+wfVfact(m)+wc(1C(m)candidates)V_{\text{composite}}(m) = w_s \cdot \mathbb{1}[\text{SchemaValid}(m)] + w_f \cdot V_{\text{fact}}(m) + w_c \cdot \left(1 - \frac{|\mathcal{C}(m)|}{|\text{candidates}|}\right)

where ws+wf+wc=1w_s + w_f + w_c = 1 and 1[]\mathbb{1}[\cdot] is the indicator function. This score is persisted alongside the memory item for downstream quality analysis.


12.3 Deduplication Strategies: Exact Match, Semantic Similarity Thresholds, Hash-Based Detection#

12.3.1 The Deduplication Imperative#

Agent loops, multi-agent orchestration, retry mechanisms, and parallel execution paths naturally produce duplicate or near-duplicate memory write attempts. Without mechanical deduplication, memory stores grow linearly with agent activity rather than linearly with novel knowledge, degrading retrieval precision and inflating token costs.

12.3.2 Three-Tier Deduplication Architecture#

The deduplication gate operates three independent detectors in sequence, ordered from cheapest to most expensive:

Tier 1: Content-Hash Exact Match (O(1) lookup)

Compute a cryptographic hash of the canonically serialized content:

h(m)=SHA-256(canonical_serialize(m.content))h(m) = \text{SHA-256}(\text{canonical\_serialize}(m.\text{content}))

If h(m)Hlh(m) \in \mathcal{H}_l (the hash set for layer ll), the write is an exact duplicate. Action: REJECT\texttt{REJECT} with reason EXACT_DUPLICATE\texttt{EXACT\_DUPLICATE}.

Tier 2: Structural Fingerprinting (O(1) lookup)

For structured memory items (key-value pairs, facts with subject-predicate-object triples), compute a structural fingerprint:

f(m)=hash(m.subject    m.predicate    normalize(m.object))f(m) = \text{hash}(m.\text{subject} \;\|\; m.\text{predicate} \;\|\; \text{normalize}(m.\text{object}))

This catches semantically identical items with minor surface variations (whitespace, casing, synonym substitution in non-critical fields).

Tier 3: Semantic Similarity Threshold (O(k) approximate nearest neighbor)

For items that pass Tiers 1 and 2, compute embedding similarity against existing items in the same semantic neighborhood:

dsem(mnew,mj)=1vmnewvmjvmnewvmjd_{\text{sem}}(m_{\text{new}}, m_j) = 1 - \frac{\vec{v}_{m_{\text{new}}} \cdot \vec{v}_{m_j}}{\|\vec{v}_{m_{\text{new}}}\| \cdot \|\vec{v}_{m_j}\|}

If mjMl\exists\, m_j \in \mathcal{M}_l such that dsem(mnew,mj)<ϵdedupd_{\text{sem}}(m_{\text{new}}, m_j) < \epsilon_{\text{dedup}} (typically ϵdedup=0.05\epsilon_{\text{dedup}} = 0.05, corresponding to cosine similarity >0.95> 0.95), the write is a near-duplicate.

Near-Duplicate Resolution:

ConditionAction
mnewm_{\text{new}} adds no new fields or informationREJECT\texttt{REJECT} (pure duplicate)
mnewm_{\text{new}} contains strictly more informationMERGE\texttt{MERGE}: update existing item, retain provenance chain
mnewm_{\text{new}} conflicts on specific fieldsDEFER\texttt{DEFER} to conflict resolution (Section 12.2.4)

12.3.3 Pseudo-Algorithm: Three-Tier Deduplication#

ALGORITHM Deduplicate(m_new: MemoryItem, layer: MemoryLayer) → DeduplicationResult
 
  // Tier 1: Exact hash match
  content_hash ← SHA256(canonical_serialize(m_new.content))
  IF hash_index[layer].contains(content_hash) THEN
    existing_ref ← hash_index[layer].get(content_hash)
    RETURN DeduplicationResult(status=REJECT, reason=EXACT_DUPLICATE, existing=existing_ref)
  END IF
 
  // Tier 2: Structural fingerprint match
  IF m_new.has_structured_triple() THEN
    fingerprint ← compute_structural_fingerprint(m_new)
    IF fingerprint_index[layer].contains(fingerprint) THEN
      existing_ref ← fingerprint_index[layer].get(fingerprint)
      delta ← compute_information_delta(m_new, existing_ref.item)
      IF delta.is_empty() THEN
        RETURN DeduplicationResult(status=REJECT, reason=STRUCTURAL_DUPLICATE, existing=existing_ref)
      ELSE IF delta.is_superset() THEN
        RETURN DeduplicationResult(status=MERGE, target=existing_ref, delta=delta)
      ELSE
        RETURN DeduplicationResult(status=DEFER, reason=STRUCTURAL_CONFLICT, existing=existing_ref, delta=delta)
      END IF
    END IF
  END IF
 
  // Tier 3: Semantic similarity
  neighbors ← vector_index[layer].approximate_nearest(m_new.embedding, top_k=10)
  FOR EACH neighbor IN neighbors DO
    sim ← cosine_similarity(m_new.embedding, neighbor.embedding)
    IF sim > (1 - ε_dedup) THEN
      delta ← compute_information_delta(m_new, neighbor.item)
      IF delta.is_empty() THEN
        RETURN DeduplicationResult(status=REJECT, reason=SEMANTIC_DUPLICATE, existing=neighbor.ref)
      ELSE IF delta.is_superset() THEN
        RETURN DeduplicationResult(status=MERGE, target=neighbor.ref, delta=delta)
      ELSE
        RETURN DeduplicationResult(status=DEFER, reason=SEMANTIC_CONFLICT, existing=neighbor.ref, delta=delta)
      END IF
    END IF
  END FOR
 
  // No duplicate detected
  hash_index[layer].insert(content_hash, m_new.ref)
  IF m_new.has_structured_triple() THEN
    fingerprint_index[layer].insert(fingerprint, m_new.ref)
  END IF
  RETURN DeduplicationResult(status=ACCEPT, is_novel=TRUE)
END ALGORITHM

12.3.4 Merge Semantics#

When a near-duplicate is identified but the new item contains additional information, a merge operation is performed. The merge must preserve:

  • The union of evidence references from both items
  • The maximum confidence score
  • The earliest creation timestamp (for provenance)
  • The latest update timestamp (for freshness)
  • A new version in the append-only log linking to both predecessors
mmerged=mexistingmnewm_{\text{merged}} = m_{\text{existing}} \oplus m_{\text{new}}

where \oplus is a field-level union operator defined per schema, with conflict resolution rules specified in the schema's merge policy.

12.3.5 Deduplication Cost Model#

The total deduplication cost per write is:

Cdedup=Chash+P(pass Tier 1)[Cfingerprint+P(pass Tier 2)Csemantic]C_{\text{dedup}} = C_{\text{hash}} + P(\text{pass Tier 1}) \cdot \left[ C_{\text{fingerprint}} + P(\text{pass Tier 2}) \cdot C_{\text{semantic}} \right]

where ChashO(1)C_{\text{hash}} \approx O(1), CfingerprintO(1)C_{\text{fingerprint}} \approx O(1), and CsemanticO(kd)C_{\text{semantic}} \approx O(k \cdot d) for kk neighbors in dd-dimensional embedding space. Since most duplicates are caught at Tier 1, the amortized cost remains near-constant.


12.4 Provenance Capture: Source Agent, Source Evidence, Confidence Score, Human Approval State#

12.4.1 The Provenance Contract#

Every memory item that survives the validation pipeline must carry a complete, immutable provenance record. Provenance is not metadata decoration—it is the basis for:

  • Trust calibration: downstream agents weigh retrieved memories by provenance quality
  • Blame attribution: when a memory causes incorrect behavior, provenance identifies the root cause
  • Audit compliance: regulatory and governance requirements demand traceable knowledge lineage
  • Conflict resolution: when contradictions arise, provenance determines precedence

12.4.2 Provenance Record Schema#

ProvenanceRecord {
  // Identity
  provenance_id         : UUID
  memory_item_id        : UUID
 
  // Source Attribution
  source_agent_id       : AgentID
  source_agent_version  : SemanticVersion
  source_task_id        : TaskID            // The task context that generated this write
  source_session_id     : SessionID
 
  // Evidence Chain
  evidence_refs         : List<EvidenceRef> // Ordered by authority
  evidence_quality      : EvidenceQuality   // {PRIMARY, SECONDARY, DERIVED, INFERRED}
  retrieval_query       : String            // The query that produced the evidence (if retrieval-sourced)
  retrieval_scores      : List<float64>     // Relevance scores at retrieval time
 
  // Confidence
  agent_confidence      : float64           // Agent's self-reported confidence ∈ [0,1]
  validation_score      : float64           // Composite validation score from Section 12.2
  verified_by_models    : List<ModelRef>     // Models used in multi-model consensus (if any)
 
  // Human Governance
  human_approval_state  : ApprovalState     // {PENDING, APPROVED, REJECTED, NOT_REQUIRED}
  approver_id           : UserID?           // Null if not human-approved
  approval_timestamp    : Timestamp?
  approval_justification: String?
 
  // Temporal
  created_at            : Timestamp
  updated_at            : Timestamp
  supersedes            : UUID?             // Previous version this item replaces
  superseded_by         : UUID?             // Newer version that replaced this item
 
  // Integrity
  content_hash          : bytes             // SHA-256 of the memory item content at write time
  provenance_signature  : bytes             // HMAC of this record for tamper detection
}

12.4.3 Evidence Reference Typing#

Each evidence reference is itself a typed record:

EvidenceRef {
  source_type    : EvidenceSourceType  // {DOCUMENT, API_RESPONSE, TOOL_OUTPUT, 
                                       //  HUMAN_INPUT, AGENT_REASONING, CODE_EXECUTION}
  source_uri     : URI                 // Canonical location of the evidence
  source_version : String              // Version/hash of the evidence at access time
  accessed_at    : Timestamp           // When the evidence was retrieved
  authority      : float64             // ∈ [0, 1], per source authority model
  excerpt        : String?             // Relevant excerpt (for auditability)
  excerpt_hash   : bytes               // Hash of excerpt for integrity
}

12.4.4 Confidence Calibration#

Agent-reported confidence is inherently unreliable (models are not well-calibrated). The system therefore computes an adjusted confidence that incorporates external signals:

cadj(m)=β1cagent(m)+β2Vfact(m)+β3evidence_authority(m)+β41[human_approved(m)]c_{\text{adj}}(m) = \beta_1 \cdot c_{\text{agent}}(m) + \beta_2 \cdot V_{\text{fact}}(m) + \beta_3 \cdot \text{evidence\_authority}(m) + \beta_4 \cdot \mathbb{1}[\text{human\_approved}(m)]

where βi=1\sum \beta_i = 1 and typical production values are:

β1=0.15,β2=0.30,β3=0.25,β4=0.30\beta_1 = 0.15, \quad \beta_2 = 0.30, \quad \beta_3 = 0.25, \quad \beta_4 = 0.30

This de-weights agent self-assessment and prioritizes empirical verification and human governance.

12.4.5 Provenance Integrity#

The provenance record is tamper-evident. At write time, an HMAC is computed:

provenance_signature=HMAC-SHA256(Kprov,canonical_serialize(ProvenanceRecord{provenance_signature}))\text{provenance\_signature} = \text{HMAC-SHA256}(K_{\text{prov}}, \text{canonical\_serialize}(\text{ProvenanceRecord} \setminus \{\text{provenance\_signature}\}))

where KprovK_{\text{prov}} is a system-managed signing key. Any subsequent alteration of the provenance record will invalidate the signature, enabling detection during audit sweeps.

12.4.6 Provenance-Weighted Retrieval#

At retrieval time, the provenance record directly influences ranking. The retrieval utility of a memory item mm is:

U(m,q)=γ1relevance(m,q)+γ2cadj(m)+γ3freshness(m)+γ4authority(m)U(m, q) = \gamma_1 \cdot \text{relevance}(m, q) + \gamma_2 \cdot c_{\text{adj}}(m) + \gamma_3 \cdot \text{freshness}(m) + \gamma_4 \cdot \text{authority}(m)

where authority(m)=maxem.evidence_refse.authority\text{authority}(m) = \max_{e \in m.\text{evidence\_refs}} e.\text{authority}. This ensures that well-provenanced memories rank higher than unsubstantiated ones, even if embedding similarity is comparable.


12.5 Memory Versioning: Append-Only Logs, Point-in-Time Queries, Rollback Capabilities#

12.5.1 Immutability Principle#

Durable memory stores must never mutate items in place. All modifications—updates, corrections, supersessions, retractions—are expressed as new versions appended to an immutable log. This provides:

  • Full audit trail of all knowledge evolution
  • Point-in-time reconstruction of the memory state at any historical moment
  • Safe rollback when a bad write is detected post-commit
  • Deterministic replay for debugging and evaluation

12.5.2 Version Graph Model#

Each memory item exists within a directed acyclic version graph (DAG):

Gv=(V,E)where V={v1,v2,} are versions, E={(vi,vj)vj supersedes vi}\mathcal{G}_v = (V, E) \quad \text{where } V = \{v_1, v_2, \ldots\} \text{ are versions, } E = \{(v_i, v_j) \mid v_j \text{ supersedes } v_i\}

The head of a version chain is the most recent active version:

head(m)=argmaxvchain(m)v.created_ats.t. v.status=ACTIVE\text{head}(m) = \arg\max_{v \in \text{chain}(m)} v.\text{created\_at} \quad \text{s.t. } v.\text{status} = \texttt{ACTIVE}

12.5.3 Version States#

Each version exists in one of the following states, governed by a state machine:

DRAFT ──▶ ACTIVE ──▶ SUPERSEDED
                  ──▶ RETRACTED
                  ──▶ EXPIRED
  • DRAFT: Written but not yet committed (pending approval)
  • ACTIVE: Committed and retrievable
  • SUPERSEDED: Replaced by a newer version; retained for history but excluded from default retrieval
  • RETRACTED: Explicitly invalidated (e.g., found to be factually incorrect)
  • EXPIRED: TTL exceeded; subject to garbage collection

12.5.4 Append-Only Log Structure#

The append-only log is the source of truth for all memory state:

MemoryLogEntry {
  log_sequence_number  : uint64          // Monotonically increasing
  operation            : MemoryOp        // {INSERT, UPDATE, SUPERSEDE, RETRACT, EXPIRE}
  item_id              : UUID            // Stable identifier across versions
  version_id           : UUID            // Unique to this version
  previous_version_id  : UUID?           // Links to predecessor (NULL for initial insert)
  content_snapshot     : MemoryItem      // Complete item state at this version
  provenance           : ProvenanceRecord
  timestamp            : Timestamp
  actor                : ActorID         // Agent or human who triggered the operation
  reason               : String          // Human-readable justification for the operation
}

12.5.5 Point-in-Time Query#

To reconstruct the memory state at an arbitrary timestamp tqt_q:

Ml(tq)={head(m,tq)  |  mitems(l)head(m,tq).status=ACTIVE}\mathcal{M}_l(t_q) = \left\{ \text{head}(m, t_q) \;\middle|\; m \in \text{items}(l) \wedge \text{head}(m, t_q).\text{status} = \texttt{ACTIVE} \right\}

where:

head(m,tq)=argmaxvchain(m)v.created_ats.t. v.created_attqv.status(tq)=ACTIVE\text{head}(m, t_q) = \arg\max_{v \in \text{chain}(m)} v.\text{created\_at} \quad \text{s.t. } v.\text{created\_at} \leq t_q \wedge v.\text{status}(t_q) = \texttt{ACTIVE}

Pseudo-Algorithm: Point-in-Time Query

ALGORITHM PointInTimeQuery(layer: MemoryLayer, t_query: Timestamp, query: Query) → List<MemoryItem>
 
  // Step 1: Identify all log entries up to t_query
  relevant_entries ← log.scan(layer=layer, up_to_lsn=log.lsn_at(t_query))
 
  // Step 2: Rebuild item state map
  item_state ← {}  // item_id → latest version as of t_query
 
  FOR EACH entry IN relevant_entries (ordered by log_sequence_number ASC) DO
    SWITCH entry.operation:
      CASE INSERT:
        item_state[entry.item_id] ← entry.content_snapshot
      CASE UPDATE, SUPERSEDE:
        item_state[entry.item_id] ← entry.content_snapshot
      CASE RETRACT:
        item_state.remove(entry.item_id)
      CASE EXPIRE:
        item_state.remove(entry.item_id)
    END SWITCH
  END FOR
 
  // Step 3: Execute query against reconstructed state
  results ← query.execute(item_state.values())
  RETURN results
END ALGORITHM

12.5.6 Rollback Mechanism#

Rollback creates a new log entry of type RETRACT\texttt{RETRACT} for the bad version and optionally re-activates a previous version:

ALGORITHM Rollback(item_id: UUID, bad_version_id: UUID, actor: ActorID, reason: String) → RollbackResult
 
  // Step 1: Validate the rollback target
  bad_version ← version_store.get(bad_version_id)
  ASSERT bad_version.item_id = item_id
  ASSERT bad_version.status = ACTIVE
 
  // Step 2: Retract the bad version
  retract_entry ← MemoryLogEntry(
    operation=RETRACT,
    item_id=item_id,
    version_id=bad_version_id,
    previous_version_id=bad_version.previous_version_id,
    timestamp=now(),
    actor=actor,
    reason=reason
  )
  log.append(retract_entry)
  version_store.set_status(bad_version_id, RETRACTED)
 
  // Step 3: Re-activate predecessor if it exists
  IF bad_version.previous_version_id IS NOT NULL THEN
    predecessor ← version_store.get(bad_version.previous_version_id)
    IF predecessor.status = SUPERSEDED THEN
      reactivate_entry ← MemoryLogEntry(
        operation=UPDATE,
        item_id=item_id,
        version_id=new_uuid(),
        previous_version_id=bad_version.previous_version_id,
        content_snapshot=predecessor.content_snapshot,
        timestamp=now(),
        actor=actor,
        reason="Reactivated after rollback of " + bad_version_id
      )
      log.append(reactivate_entry)
      RETURN RollbackResult(status=ROLLED_BACK_WITH_REACTIVATION, restored_version=predecessor.version_id)
    END IF
  END IF
 
  RETURN RollbackResult(status=ROLLED_BACK_NO_PREDECESSOR)
END ALGORITHM

12.5.7 Storage Cost Management#

Append-only logs grow indefinitely. To manage storage costs while preserving queryability:

  1. Compaction: Periodically merge consecutive versions of the same item into a single snapshot with full history preserved in a cold archive
  2. Tiered storage: Active versions in hot storage (SSD/memory), superseded versions in warm storage, expired versions in cold storage (object store)
  3. Snapshot checkpoints: Create periodic full-state snapshots to bound point-in-time query reconstruction cost:
CPIT(tq)=O ⁣(entries between tlast_snapshot and tq)C_{\text{PIT}}(t_q) = O\!\left(|\text{entries between } t_{\text{last\_snapshot}} \text{ and } t_q|\right)

12.6 Human-in-the-Loop Memory Approval: Workflows for High-Stakes Knowledge Writes#

12.6.1 The Approval Imperative#

Certain memory writes carry disproportionate downstream risk: a single incorrect entry in semantic or procedural memory can systematically bias all future reasoning for every agent that retrieves it. Human-in-the-loop (HITL) approval gates are not a concession to agent limitations—they are a governance requirement for writes that cross defined risk thresholds.

12.6.2 Risk Classification Function#

Each memory write request is assigned a risk tier based on a classification function:

R(w)=i=1nωifi(w)R(w) = \sum_{i=1}^{n} \omega_i \cdot f_i(w)

where the risk factors fif_i and weights ωi\omega_i include:

Risk Factor fif_iDescriptionTypical ωi\omega_i
Target layer riskflayerf_{\text{layer}}: SEMANTIC=0.8, PROCEDURAL=1.0, EPISODIC=0.30.25
Contradiction presencefcontraf_{\text{contra}}: 1.0 if contradictions detected, else 0.00.20
Evidence qualityfevidf_{\text{evid}}: 1.0avg evidence authority1.0 - \text{avg evidence authority}0.15
Confidence gapfconff_{\text{conf}}: 1.0cadj(m)1.0 - c_{\text{adj}}(m)0.15
Blast radiusfblastf_{\text{blast}}: estimated number of downstream consumers0.15
Noveltyfnovelf_{\text{novel}}: 1.0 if no prior knowledge in this topic area0.10

Approval Tier Mapping:

ApprovalTier(w)={AUTO_APPROVEif R(w)<0.3ASYNC_REVIEWif 0.3R(w)<0.7SYNC_BLOCKif R(w)0.7\text{ApprovalTier}(w) = \begin{cases} \texttt{AUTO\_APPROVE} & \text{if } R(w) < 0.3 \\ \texttt{ASYNC\_REVIEW} & \text{if } 0.3 \leq R(w) < 0.7 \\ \texttt{SYNC\_BLOCK} & \text{if } R(w) \geq 0.7 \end{cases}

12.6.3 Approval Workflow State Machine#

                   ┌───────────────┐
                   │  WRITE        │
                   │  SUBMITTED    │
                   └───────┬───────┘

                    ┌──────▼──────┐
                    │ RISK         │
                    │ CLASSIFICATION│
                    └──────┬──────┘

           ┌───────────────┼───────────────┐
           │               │               │
     ┌─────▼─────┐  ┌─────▼─────┐  ┌─────▼─────┐
     │ AUTO      │  │ ASYNC     │  │ SYNC      │
     │ APPROVE   │  │ REVIEW    │  │ BLOCK     │
     └─────┬─────┘  └─────┬─────┘  └─────┬─────┘
           │               │               │
           │         ┌─────▼─────┐  ┌─────▼─────┐
           │         │ PENDING   │  │ PENDING   │
           │         │ REVIEW    │  │ APPROVAL  │
           │         └─────┬─────┘  └─────┬─────┘
           │               │               │
           │        ┌──────┼──────┐  ┌─────┼──────┐
           │        │             │  │            │
           │  ┌─────▼───┐ ┌──────▼──┐ ┌────▼───┐ ┌▼────────┐
           │  │APPROVED │ │REJECTED │ │APPROVED│ │REJECTED │
           │  └─────┬───┘ └─────────┘ └────┬───┘ └─────────┘
           │        │                      │
           ▼        ▼                      ▼
     ┌─────────────────────────────────────────┐
     │           COMMIT TO DURABLE STORE       │
     └─────────────────────────────────────────┘

12.6.4 Async Review Semantics#

For ASYNC_REVIEW\texttt{ASYNC\_REVIEW}:

  1. The memory item is written to a staging area (not the active memory store)
  2. A review task is enqueued to the human review queue with metadata, contradictions, evidence, and the agent's justification
  3. The agent may proceed with its task; the memory item is not retrievable by other agents until approved
  4. If the review is not completed within the configured SLA (tslat_{\text{sla}}, typically 24–72 hours), an escalation policy fires
  5. Approved items are promoted to the active store; rejected items are logged with rejection reasons for agent learning

12.6.5 Sync Block Semantics#

For SYNC_BLOCK\texttt{SYNC\_BLOCK}:

  1. The agent's execution is suspended at the approval gate
  2. A real-time notification is dispatched to the designated approver(s) via the configured notification channel
  3. The approval request includes a deadline; if the deadline expires without response, the write is rejected by default (fail-closed)
  4. The agent receives the approval/rejection result and adjusts its plan accordingly

12.6.6 Pseudo-Algorithm: HITL Approval Gate#

ALGORITHM HITLApprovalGate(request: MemoryWriteRequest, risk_score: float64) → GateResult
 
  tier ← classify_approval_tier(risk_score)
 
  SWITCH tier:
    CASE AUTO_APPROVE:
      request.provenance.human_approval_state ← NOT_REQUIRED
      RETURN GateResult(status=ACCEPT)
 
    CASE ASYNC_REVIEW:
      staging_ref ← staging_store.write(request)
      review_task ← ReviewTask(
        item_ref=staging_ref,
        risk_score=risk_score,
        contradictions=request.validation_context.contradictions,
        evidence=request.evidence_refs,
        agent_justification=request.content.justification,
        sla_deadline=now() + t_sla,
        escalation_policy=get_escalation_policy(request.target_layer)
      )
      review_queue.enqueue(review_task)
      emit_notification(review_task, channel=ASYNC)
      RETURN GateResult(status=DEFER, retry_after=t_sla, staging_ref=staging_ref)
 
    CASE SYNC_BLOCK:
      approval_request ← ApprovalRequest(
        item=request.content,
        risk_score=risk_score,
        deadline=min(request.deadline, now() + t_sync_max),
        contradictions=request.validation_context.contradictions,
        evidence=request.evidence_refs
      )
      emit_notification(approval_request, channel=SYNC_REALTIME)
 
      // Block until response or deadline
      response ← approval_channel.await(approval_request.id, timeout=approval_request.deadline)
 
      IF response IS NULL THEN
        // Fail-closed on timeout
        RETURN GateResult(status=REJECT, reason="Approval timeout; fail-closed policy")
      ELSE IF response.decision = APPROVED THEN
        request.provenance.human_approval_state ← APPROVED
        request.provenance.approver_id ← response.approver_id
        request.provenance.approval_timestamp ← response.timestamp
        request.provenance.approval_justification ← response.justification
        RETURN GateResult(status=ACCEPT)
      ELSE
        RETURN GateResult(status=REJECT, reason=response.rejection_reason)
      END IF
  END SWITCH
END ALGORITHM

12.6.7 Approval Audit and Feedback Loop#

Every approval decision—whether automated, async, or sync—is logged to an immutable audit trail. Rejection reasons are structured and fed back into the agent's learning loop:

  • Rejections tagged with FACTUAL_ERROR\texttt{FACTUAL\_ERROR} trigger retraining signals
  • Rejections tagged with POLICY_VIOLATION\texttt{POLICY\_VIOLATION} are incorporated into the agent's policy prefill
  • Approval rates per agent, per layer, and per topic area are tracked as quality signals (Section 12.12)

12.7 Memory Garbage Collection: Automated Expiry, Relevance Decay, and Manual Curation#

12.7.1 The Entropy Problem#

Without active garbage collection, memory stores accumulate stale, irrelevant, superseded, and conflicting items that progressively degrade retrieval precision and inflate context costs. Memory garbage collection (GC) is the mechanism that maintains the signal-to-noise ratio of durable memory.

12.7.2 Relevance Decay Model#

The relevance of a memory item decays as a function of time, access frequency, and downstream impact:

ρ(m,t)=ρ0(m)exp ⁣(λt(ttcreated(m)))temporal decay(1+log(1+naccess(m,Δt)))access reinforcementimpact(m)downstream utility\rho(m, t) = \rho_0(m) \cdot \underbrace{\exp\!\left(-\lambda_t \cdot (t - t_{\text{created}}(m))\right)}_{\text{temporal decay}} \cdot \underbrace{\left(1 + \log(1 + n_{\text{access}}(m, \Delta t))\right)}_{\text{access reinforcement}} \cdot \underbrace{\text{impact}(m)}_{\text{downstream utility}}

where:

  • ρ0(m)\rho_0(m) is the initial relevance at write time (derived from validation score and provenance quality)
  • λt\lambda_t is the decay rate (configurable per layer; e.g., λt=0.01day1\lambda_t = 0.01 \text{day}^{-1} for episodic, λt=0.001day1\lambda_t = 0.001 \text{day}^{-1} for semantic)
  • naccess(m,Δt)n_{\text{access}}(m, \Delta t) is the number of times mm was retrieved in the trailing window Δt\Delta t
  • impact(m)[0,1]\text{impact}(m) \in [0, 1] measures whether retrievals of mm led to successful task completions

12.7.3 GC Eligibility Criteria#

A memory item mm is eligible for garbage collection when:

ρ(m,tnow)<τgc(l)tnowtlast_access(m)>Tidle(l)m.statusPINNED\rho(m, t_{\text{now}}) < \tau_{\text{gc}}(l) \quad \wedge \quad t_{\text{now}} - t_{\text{last\_access}}(m) > T_{\text{idle}}(l) \quad \wedge \quad m.\text{status} \neq \texttt{PINNED}

where τgc(l)\tau_{\text{gc}}(l) is the layer-specific GC threshold and Tidle(l)T_{\text{idle}}(l) is the maximum idle period.

Layerτgc\tau_{\text{gc}}TidleT_{\text{idle}}λt\lambda_t
WorkingN/A (auto-cleared at session end)0N/A
Session0.11 hour0.5 h⁻¹
Episodic0.230 days0.01 day⁻¹
Semantic0.1180 days0.001 day⁻¹
Procedural0.1365 days0.0005 day⁻¹

12.7.4 GC Execution Modes#

Mode 1: TTL-Based Expiry (Mechanical)

Items with explicit TTL values are expired deterministically:

expired(m)=(tnow>tcreated(m)+m.ttl)\text{expired}(m) = (t_{\text{now}} > t_{\text{created}}(m) + m.\text{ttl})

Mode 2: Relevance-Based GC (Analytical)

A background GC agent periodically scans memory layers and marks low-relevance items for collection:

ALGORITHM RelevanceGC(layer: MemoryLayer, batch_size: int) → GCReport
 
  scan_cursor ← layer.gc_cursor  // Resume from last position
  candidates ← []
  scanned ← 0
 
  WHILE scanned < batch_size DO
    item ← layer.next(scan_cursor)
    IF item IS NULL THEN BREAK END IF
 
    ρ ← compute_relevance(item, now())
 
    IF ρ < τ_gc(layer) AND time_since_last_access(item) > T_idle(layer) AND item.status ≠ PINNED THEN
      candidates.append(GCCandidate(item_id=item.id, relevance=ρ, last_access=item.last_access))
    END IF
 
    scan_cursor ← scan_cursor.advance()
    scanned ← scanned + 1
  END WHILE
 
  // Phase 1: Soft delete (mark as EXPIRED, retain in cold storage)
  FOR EACH candidate IN candidates DO
    log.append(MemoryLogEntry(operation=EXPIRE, item_id=candidate.item_id, timestamp=now()))
    version_store.set_status(candidate.item_id, EXPIRED)
    cold_store.archive(candidate.item_id)
  END FOR
 
  layer.gc_cursor ← scan_cursor
  emit_metric("memory.gc.collected", {layer: layer.name, count: |candidates|})
  RETURN GCReport(scanned=scanned, collected=|candidates|, next_cursor=scan_cursor)
END ALGORITHM

Mode 3: Manual Curation

Human curators can:

  • Pin items to prevent GC (m.statusPINNEDm.\text{status} \leftarrow \texttt{PINNED})
  • Force-expire items that are identified as harmful or stale
  • Reclassify items between memory layers (e.g., promote episodic → semantic after validation)
  • Annotate items with curation notes that influence future relevance scoring

12.7.5 Two-Phase Collection#

GC operates in two phases to prevent accidental data loss:

  1. Soft delete (Phase 1): Item is marked EXPIRED\texttt{EXPIRED}, removed from active indices, but retained in cold storage for a configurable grace period TgraceT_{\text{grace}}
  2. Hard delete (Phase 2): After TgraceT_{\text{grace}} elapses without a recovery request, the item is permanently removed from cold storage
hard_delete(m)=(m.status=EXPIRED)(tnowtexpired(m)>Tgrace)\text{hard\_delete}(m) = \left( m.\text{status} = \texttt{EXPIRED} \right) \wedge \left( t_{\text{now}} - t_{\text{expired}}(m) > T_{\text{grace}} \right)

This ensures that incorrectly collected items can be recovered within the grace window.


12.8 Cross-Agent Memory Sharing: Access Control, Read/Write Permissions, and Lease-Based Locks#

12.8.1 Shared Memory as a Coordination Primitive#

In multi-agent systems, shared memory is both a knowledge resource and a coordination mechanism. Without explicit access control and concurrency management, shared memory becomes a vector for:

  • Race conditions: two agents simultaneously writing conflicting values
  • Context poisoning: a compromised or malfunctioning agent corrupting shared state
  • Privilege escalation: an agent reading memory it should not have access to
  • Starvation: one agent monopolizing write access

12.8.2 Access Control Model#

The access control model follows a role-based access control (RBAC) scheme layered with attribute-based access control (ABAC) for fine-grained policies:

authorized(a,m,op)=RBAC(a.role,m.layer,op)ABAC(a.attributes,m.attributes,op)\text{authorized}(a, m, \text{op}) = \text{RBAC}(a.\text{role}, m.\text{layer}, \text{op}) \wedge \text{ABAC}(a.\text{attributes}, m.\text{attributes}, \text{op})

where op{READ,WRITE,DELETE,ADMIN}\text{op} \in \{\texttt{READ}, \texttt{WRITE}, \texttt{DELETE}, \texttt{ADMIN}\}.

RBAC Role Definitions:

RoleREADWRITEDELETEADMIN
Executor AgentOwn session + shared episodicOwn session onlyNoneNone
Verifier AgentAll layers (read-only)Validation annotationsNoneNone
OrchestratorAll layersEpisodic + sessionSoft deleteLayer config
Curator (Human)All layersAll layersHard deleteFull

ABAC Policy Example:

Policy: "Medical agents may only write to memory items tagged with domain=medical"
  Condition: agent.domain_specialization = "medical"
             AND item.domain_tag = "medical"
             AND operation = WRITE
  Effect: ALLOW

12.8.3 Lease-Based Write Locks#

To prevent concurrent write conflicts on shared memory regions, the system implements lease-based distributed locks:

Lease(a,m,Tlease)={GRANTEDif m is unlocked or lease expiredDENIEDif m is locked by another agent with active lease\text{Lease}(a, m, T_{\text{lease}}) = \begin{cases} \texttt{GRANTED} & \text{if } m \text{ is unlocked or lease expired} \\ \texttt{DENIED} & \text{if } m \text{ is locked by another agent with active lease} \end{cases}

Lease Properties:

  • Bounded duration: Every lease has a maximum TTL (TleaseT_{\text{lease}}, typically 30–120 seconds) to prevent deadlocks from agent failures
  • Renewable: The holding agent may renew the lease before expiry if work is ongoing
  • Fencing tokens: Each lease grant includes a monotonically increasing fencing token ϕ\phi to prevent stale-lease writes:
write_valid(w)=(w.fencing_tokencurrent_fencing_token(m))\text{write\_valid}(w) = (w.\text{fencing\_token} \geq \text{current\_fencing\_token}(m))

Pseudo-Algorithm: Lease Acquisition

ALGORITHM AcquireLease(agent_id: AgentID, item_id: UUID, duration: Duration) → LeaseResult
 
  lock_entry ← lock_store.get(item_id)
 
  IF lock_entry IS NULL OR lock_entry.expires_at < now() THEN
    // Lock is available (either unset or expired)
    new_fencing_token ← fencing_counter.increment(item_id)
    new_lease ← Lease(
      agent_id=agent_id,
      item_id=item_id,
      fencing_token=new_fencing_token,
      granted_at=now(),
      expires_at=now() + duration,
      renewable=TRUE
    )
    lock_store.put(item_id, new_lease)
    RETURN LeaseResult(status=GRANTED, lease=new_lease)
 
  ELSE IF lock_entry.agent_id = agent_id THEN
    // Re-entrant: same agent already holds the lock
    lock_entry.expires_at ← now() + duration
    lock_store.put(item_id, lock_entry)
    RETURN LeaseResult(status=RENEWED, lease=lock_entry)
 
  ELSE
    // Lock held by another agent
    RETURN LeaseResult(status=DENIED, holder=lock_entry.agent_id,
                       retry_after=lock_entry.expires_at)
  END IF
END ALGORITHM

12.8.4 Workspace Isolation Pattern#

For complex multi-agent tasks, each agent operates on an isolated workspace (analogous to a Git branch) rather than writing directly to shared memory:

  1. Agent claims a task and creates a workspace (copy-on-write snapshot of relevant shared memory)
  2. Agent performs all reads and writes within the workspace
  3. Upon task completion, the agent submits a merge request to integrate workspace changes into shared memory
  4. A merge arbiter (orchestrator or dedicated agent) reviews the merge for conflicts
  5. Accepted changes are committed; conflicting changes are flagged for resolution
Workspace(a)=COW_Snapshot(Mshared)Δa\text{Workspace}(a) = \text{COW\_Snapshot}(\mathcal{M}_{\text{shared}}) \oplus \Delta_a

where Δa\Delta_a represents the local modifications made by agent aa. The merge operation is:

Mshared=Msharedresolve(Δa1,Δa2,)\mathcal{M}_{\text{shared}}' = \mathcal{M}_{\text{shared}} \oplus \text{resolve}(\Delta_{a_1}, \Delta_{a_2}, \ldots)

The resolve function applies a configurable conflict resolution strategy: last-writer-wins, highest-confidence-wins, or human-mediated.

12.8.5 Read Permissions and Visibility Scoping#

Not all agents should see all memories. The visibility scope is computed as:

visible(a,m)=authorized(a,m,READ)classification(m)clearance(a)domain(m)domains(a)\text{visible}(a, m) = \text{authorized}(a, m, \texttt{READ}) \wedge \text{classification}(m) \leq \text{clearance}(a) \wedge \text{domain}(m) \subseteq \text{domains}(a)

This prevents:

  • Cross-domain information leakage (e.g., a financial agent seeing medical memory)
  • Privilege escalation through retrieval (an agent gaining knowledge it was not intended to possess)
  • Inadvertent exposure of PII to agents without appropriate authorization

12.9 Memory Consistency Models: Eventual, Causal, and Strong Consistency Trade-offs#

12.9.1 The Consistency Challenge#

In distributed multi-agent systems, where agents may be running on different compute nodes, accessing replicated memory stores, and operating with different latency budgets, the question of memory consistency is non-trivial. The choice of consistency model directly impacts correctness, latency, and availability.

12.9.2 Consistency Model Spectrum#

Strong Consistency (Linearizability)

Every read returns the value of the most recent write, as if all operations were serialized on a single node.

rreads(m):r.value=Wlatest(m,r.timestamp)\forall\, r \in \text{reads}(m) : r.\text{value} = W_{\text{latest}}(m, r.\text{timestamp})
  • Advantage: No stale reads; reasoning is always based on the latest state
  • Disadvantage: High latency (requires coordination), reduced availability under partitions
  • Use case: Procedural memory (agent policies, tool schemas, safety constraints)

Causal Consistency

If write w1w_1 causally precedes write w2w_2 (i.e., the agent that performed w2w_2 observed w1w_1), then all agents observe w1w_1 before w2w_2.

w1causesw2    a:observe(a,w1)observe(a,w2)w_1 \xrightarrow{\text{causes}} w_2 \implies \forall\, a : \text{observe}(a, w_1) \prec \text{observe}(a, w_2)
  • Advantage: Preserves logical ordering without global coordination
  • Disadvantage: Concurrent, causally unrelated writes may be observed in different orders by different agents
  • Use case: Episodic memory (agent experiences have natural causal ordering)

Eventual Consistency

All replicas will converge to the same state given sufficient time without new writes.

limtstate(r1,t)=state(r2,t)==state(rn,t)\lim_{t \to \infty} \text{state}(r_1, t) = \text{state}(r_2, t) = \ldots = \text{state}(r_n, t)
  • Advantage: Lowest latency, highest availability
  • Disadvantage: Agents may reason on stale data; requires conflict resolution for concurrent writes
  • Use case: Session memory, non-critical episodic data, cached retrieval results

12.9.3 Layer-Consistency Mapping#

The system assigns consistency models per memory layer based on the criticality and access pattern:

Memory LayerConsistency ModelJustification
WorkingN/A (agent-local, no replication)Single-writer, no sharing
SessionEventualLow-risk; session-scoped; single primary agent
EpisodicCausalExperiences have causal ordering; concurrent agents may share
SemanticStrongCanonical knowledge must be globally consistent
ProceduralStrongPolicies and procedures must be immediately consistent

12.9.4 Conflict Resolution under Eventual Consistency#

When eventual consistency is used, concurrent writes to the same item may produce conflicts. The system supports pluggable conflict resolution strategies:

Last-Writer-Wins (LWW):

mresolved=argmaxwconcurrent_writes(m)w.timestampm_{\text{resolved}} = \arg\max_{w \in \text{concurrent\_writes}(m)} w.\text{timestamp}

Simple but may discard valid information.

Highest-Confidence-Wins (HCW):

mresolved=argmaxwconcurrent_writes(m)cadj(w)m_{\text{resolved}} = \arg\max_{w \in \text{concurrent\_writes}(m)} c_{\text{adj}}(w)

Prefers writes with better provenance and validation scores.

CRDTs (Conflict-Free Replicated Data Types):

For memory items modeled as sets (e.g., tag sets, evidence reference lists), use CRDT semantics to achieve automatic merge:

GSet:merge(S1,S2)=S1S2\text{GSet}: \text{merge}(S_1, S_2) = S_1 \cup S_2 ORSet:merge(S1,S2)=(S1.addsS2.adds)(S1.removesS2.removes)\text{ORSet}: \text{merge}(S_1, S_2) = (S_1.\text{adds} \cup S_2.\text{adds}) \setminus (S_1.\text{removes} \cup S_2.\text{removes})

Human-Mediated Resolution:

For high-stakes conflicts that cannot be resolved mechanically, the system escalates to human review (Section 12.6).

12.9.5 Consistency Verification Agent#

A dedicated background agent periodically verifies memory consistency across replicas:

ALGORITHM ConsistencyVerifier(layer: MemoryLayer, sample_rate: float64) → VerificationReport
 
  items ← layer.sample(rate=sample_rate)
  inconsistencies ← []
 
  FOR EACH item IN items DO
    replica_values ← []
    FOR EACH replica IN layer.replicas DO
      replica_values.append(replica.read(item.id))
    END FOR
 
    IF NOT all_equal(replica_values) THEN
      inconsistencies.append(InconsistencyRecord(
        item_id=item.id,
        divergent_replicas=find_divergent(replica_values),
        detected_at=now()
      ))
    END IF
  END FOR
 
  IF inconsistencies IS NOT EMPTY THEN
    FOR EACH inc IN inconsistencies DO
      resolution ← apply_conflict_resolution(inc, layer.conflict_strategy)
      layer.force_reconcile(inc.item_id, resolution)
    END FOR
  END IF
 
  emit_metric("memory.consistency.check", {
    layer: layer.name,
    sampled: |items|,
    inconsistencies: |inconsistencies|,
    rate: |inconsistencies| / |items|
  })
 
  RETURN VerificationReport(sampled=|items|, inconsistencies=inconsistencies)
END ALGORITHM

12.9.6 Read Staleness Budget#

For layers with eventual consistency, the system defines a staleness budget δstale\delta_{\text{stale}}—the maximum acceptable delay between a write and its visibility to other agents:

P(read_staleness(m)>δstale)<ϵstaleP(\text{read\_staleness}(m) > \delta_{\text{stale}}) < \epsilon_{\text{stale}}

where ϵstale\epsilon_{\text{stale}} is the acceptable staleness violation rate (e.g., ϵstale=0.01\epsilon_{\text{stale}} = 0.01). The replication pipeline is tuned to meet this SLO.


12.10 Regulatory Compliance: GDPR Right-to-Erasure, Data Residency, and Memory Retention Policies#

12.10.1 Regulatory Imperatives#

Agentic memory systems that ingest, store, or reason over personal data, user interactions, or business-sensitive information are subject to regulatory frameworks including GDPR, CCPA, HIPAA, and domain-specific regulations. Memory governance must mechanically enforce compliance, not rely on procedural controls.

12.10.2 Right-to-Erasure (GDPR Article 17)#

Upon receiving a valid erasure request for data subject ss, the system must:

  1. Identify all memory items containing or derived from ss's personal data
  2. Erase both the primary items and any derived items (including embeddings, summaries, and aggregations)
  3. Verify erasure completeness across all replicas, caches, and cold storage tiers
  4. Certify erasure with an audit log entry

Formal Erasure Scope. Let PII(s)\text{PII}(s) denote the set of personal data identifiers for subject ss. The erasure set is:

E(s)={mM  |  contains_pii(m,PII(s))derived_from(m,E(s))}\mathcal{E}(s) = \left\{ m \in \mathcal{M} \;\middle|\; \text{contains\_pii}(m, \text{PII}(s)) \vee \text{derived\_from}(m, \mathcal{E}(s)) \right\}

Note the recursive definition: derived items (summaries, embeddings, aggregations) that incorporate data from ss must also be erased or regenerated without ss's data.

Pseudo-Algorithm: Right-to-Erasure Execution

ALGORITHM ExecuteErasure(subject_id: SubjectID, request_id: UUID) → ErasureReport
 
  // Step 1: PII identification
  pii_identifiers ← pii_registry.get_identifiers(subject_id)
 
  // Step 2: Forward scan — find all directly containing items
  direct_matches ← []
  FOR EACH layer IN all_memory_layers DO
    FOR EACH item IN layer.full_scan() DO
      IF contains_any_pii(item.content, pii_identifiers) THEN
        direct_matches.append(item)
      END IF
    END FOR
  END FOR
 
  // Step 3: Transitive closure — find derived items
  erasure_set ← direct_matches
  frontier ← direct_matches
  WHILE frontier IS NOT EMPTY DO
    next_frontier ← []
    FOR EACH item IN frontier DO
      derived ← derivation_graph.get_dependents(item.id)
      FOR EACH d IN derived DO
        IF d ∉ erasure_set THEN
          erasure_set.append(d)
          next_frontier.append(d)
        END IF
      END FOR
    END FOR
    frontier ← next_frontier
  END WHILE
 
  // Step 4: Execute erasure across all tiers
  FOR EACH item IN erasure_set DO
    FOR EACH replica IN get_all_replicas(item) DO
      replica.hard_delete(item.id)
    END FOR
    vector_index.remove(item.id)
    cache.invalidate(item.id)
    cold_store.hard_delete(item.id)
  END FOR
 
  // Step 5: Purge embeddings derived from PII
  embedding_store.purge_by_source(pii_identifiers)
 
  // Step 6: Audit log (itself must not contain PII)
  audit_log.append(ErasureAuditEntry(
    request_id=request_id,
    subject_id_hash=SHA256(subject_id),  // Hash, not raw PII
    items_erased=|erasure_set|,
    layers_affected=distinct_layers(erasure_set),
    completed_at=now(),
    verified=TRUE
  ))
 
  RETURN ErasureReport(items_erased=|erasure_set|, status=COMPLETED)
END ALGORITHM

12.10.3 Data Residency Constraints#

Memory items may be subject to geographic residency requirements (e.g., EU data must remain within EU data centers). The memory system enforces this through:

residency_valid(m)=storage_region(m)allowed_regions(m.residency_class)\text{residency\_valid}(m) = \text{storage\_region}(m) \in \text{allowed\_regions}(m.\text{residency\_class})

Write requests that would violate residency constraints are rejected at the policy gate. Replication topologies are configured to ensure that items tagged with specific residency classes are never replicated outside permitted regions.

12.10.4 Retention Policies#

Each memory layer and item classification has a maximum retention period, beyond which the item must be expired regardless of relevance:

retention_expired(m)=(tnowtcreated(m))>Tmax_retention(m.classification)\text{retention\_expired}(m) = (t_{\text{now}} - t_{\text{created}}(m)) > T_{\text{max\_retention}}(m.\text{classification})

Retention policies override relevance-based GC: even a frequently accessed, high-relevance item must be expired if its retention period has elapsed.

Data ClassificationMax RetentionPost-Expiry Action
PII-containing90 days (or per consent)Hard delete + audit
Business-sensitive1 yearSoft delete → archive
General knowledge5 yearsSoft delete → cold storage
Public/open-sourceIndefiniteRelevance GC only

12.10.5 Compliance Verification Agent#

A periodic compliance agent audits the memory store:

  1. Scan for items exceeding retention limits
  2. Verify that all erasure requests have been fully executed
  3. Validate residency constraints across replicas
  4. Generate compliance reports for governance review
  5. Flag violations as P0\texttt{P0} incidents with automated remediation

12.11 Memory Anti-Patterns: Unchecked Growth, Hallucinated Memories, Circular Reinforcement, Context Poisoning#

12.11.1 Taxonomy of Memory Anti-Patterns#

Memory anti-patterns are failure modes that emerge when write policies are insufficient, validation is incomplete, or governance is absent. Each anti-pattern has a distinct mechanism, a characteristic signal, and a specific mitigation.

12.11.2 Anti-Pattern 1: Unchecked Growth#

Mechanism: Agents write to durable memory without selectivity. Every observation, intermediate result, and reasoning step is persisted, causing memory to grow linearly with computation rather than with novel knowledge.

Characteristic Signal:

dMldtdnovel_knowledgedt\frac{d|\mathcal{M}_l|}{dt} \gg \frac{d|\text{novel\_knowledge}|}{dt}

The ratio of total memory items to unique knowledge units grows unboundedly.

Impact:

  • Retrieval precision degrades (needle-in-haystack problem)
  • Token costs for memory summarization escalate
  • Context window is saturated with redundant information

Mitigation:

  • Enforce per-agent and per-layer write rate limits (Section 12.1.8)
  • Apply strict deduplication at write time (Section 12.3)
  • Implement relevance-based GC with aggressive decay rates for high-volume layers (Section 12.7)
  • Define a memory budget per agent per session:
writes(a,session)Bwrite(a.role)|\text{writes}(a, \text{session})| \leq B_{\text{write}}(a.\text{role})

12.11.3 Anti-Pattern 2: Hallucinated Memories#

Mechanism: An agent generates a plausible but factually incorrect statement during reasoning, then writes it to durable memory. Subsequent retrievals treat the hallucinated item as ground truth, compounding errors.

Characteristic Signal:

Vfact(m)<τfact(l)but m was admitted due to missing evidence cross-checkV_{\text{fact}}(m) < \tau_{\text{fact}}(l) \quad \text{but } m \text{ was admitted due to missing evidence cross-check}

Or more insidiously: Vfact(m)V_{\text{fact}}(m) appears adequate because the supporting evidence was itself hallucinated or from a low-authority source.

Impact:

  • Permanent epistemic corruption
  • Error propagation through retrieval → reasoning → further writes
  • Loss of trust in the entire memory system

Mitigation:

  • Require minimum evidence authority thresholds for durable writes:
minem.evidence_refsauthority(e)αmin(l)\min_{e \in m.\text{evidence\_refs}} \text{authority}(e) \geq \alpha_{\text{min}}(l)
  • Reject writes where all evidence originates from the writing agent itself (self-referential evidence prohibition):
em.evidence_refs:e.source_typeAGENT_REASONINGfor layers {SEMANTIC,PROCEDURAL}\forall\, e \in m.\text{evidence\_refs} : e.\text{source\_type} \neq \texttt{AGENT\_REASONING} \quad \text{for layers } \in \{\text{SEMANTIC}, \text{PROCEDURAL}\}
  • Multi-model consensus for high-tier writes (Section 12.2.3)
  • Periodic memory audit sweeps that re-verify factual claims against updated knowledge bases

12.11.4 Anti-Pattern 3: Circular Reinforcement#

Mechanism: Agent A writes a claim to memory. Agent B retrieves this claim, treats it as evidence, and writes a derived claim. Agent A later retrieves Agent B's derived claim as independent confirmation, reinforcing the original (possibly incorrect) claim with circular evidence.

Formal Definition. A circular reinforcement cycle exists when:

m1evidence form2evidence forevidence formkevidence form1m_1 \xrightarrow{\text{evidence for}} m_2 \xrightarrow{\text{evidence for}} \cdots \xrightarrow{\text{evidence for}} m_k \xrightarrow{\text{evidence for}} m_1

Characteristic Signal: The provenance graph contains cycles:

cycle in Gprovenance=(Vitems,Eevidence)\exists\, \text{cycle in } \mathcal{G}_{\text{provenance}} = (V_{\text{items}}, E_{\text{evidence}})

Impact:

  • False confidence amplification (each item in the cycle appears well-supported)
  • Resistant to correction (removing one item still leaves the rest apparently valid)

Mitigation:

  • Cycle detection at write time: Before admitting a new item, traverse the evidence provenance graph and reject if the new item would create a cycle:
ALGORITHM DetectProvenanceCycle(m_new: MemoryItem) → Boolean
  // Perform DFS from m_new's evidence refs looking for m_new itself
  visited ← {}
  stack ← [e.source_item_id FOR e IN m_new.evidence_refs IF e.source_type = MEMORY_ITEM]
 
  WHILE stack IS NOT EMPTY DO
    current ← stack.pop()
    IF current = m_new.id THEN RETURN TRUE END IF  // Cycle detected
    IF current ∈ visited THEN CONTINUE END IF
    visited.add(current)
 
    current_item ← memory_store.get(current)
    IF current_item IS NOT NULL THEN
      FOR EACH e IN current_item.evidence_refs DO
        IF e.source_type = MEMORY_ITEM THEN
          stack.push(e.source_item_id)
        END IF
      END FOR
    END IF
  END WHILE
 
  RETURN FALSE  // No cycle
END ALGORITHM
  • Evidence diversity requirement: Require that at least one evidence source is external (non-memory-derived) for durable writes:
{em.evidence_refse.source_type{DOCUMENT,API_RESPONSE,HUMAN_INPUT}}1|\{e \in m.\text{evidence\_refs} \mid e.\text{source\_type} \in \{\texttt{DOCUMENT}, \texttt{API\_RESPONSE}, \texttt{HUMAN\_INPUT}\}\}| \geq 1

12.11.5 Anti-Pattern 4: Context Poisoning#

Mechanism: A malicious or malfunctioning agent deliberately writes misleading information to shared memory, exploiting the trust other agents place in retrieved memory.

Characteristic Signal:

  • Sudden increase in contradiction detection rates after writes from a specific agent
  • Downstream task failure rates correlated with retrievals from specific memory items
  • Write patterns inconsistent with the agent's declared task

Impact:

  • Adversarial manipulation of multi-agent reasoning
  • Difficulty distinguishing poisoned items from legitimate knowledge
  • Systemic trust degradation

Mitigation:

  • Agent reputation scoring: Track per-agent write acceptance rate, downstream correctness impact, and contradiction rates. Agents with degraded reputation are subject to stricter validation:
reputation(a)=wwrites(a)1[correct(w)]wwrites(a)1\text{reputation}(a) = \frac{\sum_{w \in \text{writes}(a)} \mathbb{1}[\text{correct}(w)]}{\sum_{w \in \text{writes}(a)} 1}

Agents with reputation(a)<θrep\text{reputation}(a) < \theta_{\text{rep}} are downgraded to requiring HITL approval for all writes.

  • Write anomaly detection: Monitor for statistical anomalies in write patterns (burst writes, unusual topic distribution, high contradiction rate) and trigger automatic write suspension
  • Least-privilege write access: Agents only have write access to memory layers and topic domains relevant to their assigned task (Section 12.8.2)
  • Quarantine on detection: Suspected poisoned items are quarantined (removed from active retrieval, retained for investigation) and all dependent items are flagged for re-validation

12.11.6 Anti-Pattern Summary Matrix#

Anti-PatternRoot CauseDetection SignalPrimary Mitigation
Unchecked growthMissing selectivityGrowth rate ≫ novelty rateRate limits + GC
Hallucinated memoriesInsufficient verificationLow evidence authority; self-referentialMulti-source evidence requirement
Circular reinforcementProvenance cyclesCycles in evidence graphCycle detection + external evidence requirement
Context poisoningAdversarial or buggy agentsContradiction spikes; downstream failuresReputation scoring + anomaly detection + quarantine

12.12 Memory Quality Metrics: Precision of Recall, Write Acceptance Rate, Correctness Impact on Downstream Tasks#

12.12.1 The Measurement Imperative#

Memory quality cannot be managed without measurement. The memory system must continuously compute, track, and alert on a comprehensive set of quality metrics that capture the precision, accuracy, utility, and health of durable memory.

12.12.2 Metric Taxonomy#

The metrics are organized into four categories:

  1. Write-Path Metrics — quality of the admission process
  2. Retrieval-Quality Metrics — utility of memory at retrieval time
  3. Downstream-Impact Metrics — effect of memory on task outcomes
  4. Operational Health Metrics — system-level health of the memory infrastructure

12.12.3 Write-Path Metrics#

Write Acceptance Rate (WAR):

WAR(l,Δt)={wwrites(l,Δt)w.status=COMMITTED}writes(l,Δt)\text{WAR}(l, \Delta t) = \frac{|\{w \in \text{writes}(l, \Delta t) \mid w.\text{status} = \texttt{COMMITTED}\}|}{|\text{writes}(l, \Delta t)|}

A WAR that is too high (>0.95> 0.95) suggests insufficient filtering. A WAR that is too low (<0.3< 0.3) suggests over-restrictive policies or low-quality agent outputs.

Target Range: 0.4WAR0.80.4 \leq \text{WAR} \leq 0.8 for semantic and procedural layers.

Rejection Reason Distribution:

P(reason=rrejected)={ww.rejection_reason=r}{ww.status=REJECTED}P(\text{reason} = r \mid \text{rejected}) = \frac{|\{w \mid w.\text{rejection\_reason} = r\}|}{|\{w \mid w.\text{status} = \texttt{REJECTED}\}|}

Track the distribution over time to identify systematic quality issues (e.g., a spike in CONTRADICTION\texttt{CONTRADICTION} rejections indicates conflicting knowledge sources).

Validation Latency:

Lvalidate(p)=percentilep ⁣({tvalidation_completetwrite_submittedwwrites})L_{\text{validate}}(p) = \text{percentile}_{p}\!\left(\{t_{\text{validation\_complete}} - t_{\text{write\_submitted}} \mid w \in \text{writes}\}\right)

Track p50p_{50}, p95p_{95}, and p99p_{99} latencies per validation stage.

Deduplication Hit Rate:

DHR(Δt)={wwrites(Δt)w.dedup_result{EXACT_DUPLICATE,SEMANTIC_DUPLICATE}}writes(Δt)\text{DHR}(\Delta t) = \frac{|\{w \in \text{writes}(\Delta t) \mid w.\text{dedup\_result} \in \{\texttt{EXACT\_DUPLICATE}, \texttt{SEMANTIC\_DUPLICATE}\}\}|}{|\text{writes}(\Delta t)|}

A high DHR (>0.3> 0.3) indicates that agents are generating excessive redundant writes, suggesting upstream context or planning improvements are needed.

12.12.4 Retrieval-Quality Metrics#

Memory Retrieval Precision (MRP):

The fraction of retrieved memory items that were actually useful (cited, used in reasoning, or contributed to the final output):

MRP(q)=retrieved(q)used(q)retrieved(q)\text{MRP}(q) = \frac{|\text{retrieved}(q) \cap \text{used}(q)|}{|\text{retrieved}(q)|}

Averaged over all queries in a time window:

MRP(Δt)=1Q(Δt)qQ(Δt)MRP(q)\overline{\text{MRP}}(\Delta t) = \frac{1}{|Q(\Delta t)|} \sum_{q \in Q(\Delta t)} \text{MRP}(q)

Target: MRP0.7\overline{\text{MRP}} \geq 0.7

Memory Retrieval Recall (MRR):

The fraction of useful memory items that were successfully retrieved:

MRR(q)=retrieved(q)relevant(q)relevant(q)\text{MRR}(q) = \frac{|\text{retrieved}(q) \cap \text{relevant}(q)|}{|\text{relevant}(q)|}

This requires ground-truth labeling (from human annotation or downstream task success) and is measured through periodic evaluation sweeps.

Staleness at Retrieval:

S(m,tq)=tqtlast_updated(m)S(m, t_q) = t_q - t_{\text{last\_updated}}(m)

Track the distribution of staleness across retrieved items. High median staleness indicates that GC or refresh policies are insufficient.

12.12.5 Downstream-Impact Metrics#

Memory Correctness Impact (MCI):

The causal effect of memory retrieval on task correctness. Measured through ablation:

MCI=P(task_correctmemory_retrieved)P(task_correctmemory_ablated)\text{MCI} = P(\text{task\_correct} \mid \text{memory\_retrieved}) - P(\text{task\_correct} \mid \text{memory\_ablated})

A positive MCI indicates that memory is net-beneficial. A negative MCI indicates that memory is actively harmful (likely due to hallucinated or stale items) and triggers immediate investigation.

Memory-Induced Error Rate (MIER):

MIER(Δt)={ttasks(Δt)t.failedroot_cause(t)memory_items}tasks(Δt)\text{MIER}(\Delta t) = \frac{|\{t \in \text{tasks}(\Delta t) \mid t.\text{failed} \wedge \text{root\_cause}(t) \in \text{memory\_items}\}|}{|\text{tasks}(\Delta t)|}

Tracks the rate at which task failures are attributable to incorrect memory items. Requires root-cause analysis infrastructure (traces, provenance backtracking).

Target: MIER<0.01\text{MIER} < 0.01

Hallucination Leakage Rate (HLR):

HLR(Δt)={mMcommitted(Δt)retrospectively_verified(m)=FALSE}Mcommitted(Δt)\text{HLR}(\Delta t) = \frac{|\{m \in \mathcal{M}_{\text{committed}}(\Delta t) \mid \text{retrospectively\_verified}(m) = \texttt{FALSE}\}|}{|\mathcal{M}_{\text{committed}}(\Delta t)|}

Measured by periodically re-validating a sample of committed memory items against updated evidence. An HLR >0.05> 0.05 triggers tightening of validation thresholds.

12.12.6 Operational Health Metrics#

MetricFormulaTarget
Memory store size per layer$\\mathcal{M}_l\
GC throughputItems collected per GC cycleMatches growth rate within 10%
Replication lagtreplicatprimaryt_{\text{replica}} - t_{\text{primary}}<δstale< \delta_{\text{stale}} (Section 12.9.6)
Lock contention rateLease denials / total lease requests<0.1< 0.1
Erasure completion timeterasure_completeterasure_requestedt_{\text{erasure\_complete}} - t_{\text{erasure\_requested}}<72< 72 hours (GDPR)
Consistency violation rateInconsistencies / sampled items<ϵstale< \epsilon_{\text{stale}}

12.12.7 Composite Memory Health Score#

A single composite score summarizes overall memory system health:

Hmemory=i=1kwinormalized(Mi)H_{\text{memory}} = \sum_{i=1}^{k} w_i \cdot \text{normalized}(M_i)

where MiM_i are the individual metrics, wiw_i are importance weights (wi=1\sum w_i = 1), and normalized()\text{normalized}(\cdot) maps each metric to [0,1][0, 1] with 1 being optimal.

Suggested Weights:

MetricWeight
Memory Correctness Impact0.25
Memory Retrieval Precision0.20
Hallucination Leakage Rate (inverted)0.20
Memory-Induced Error Rate (inverted)0.15
Write Acceptance Rate (distance from target range)0.10
Operational health composite0.10

Alert Thresholds:

Hmemory0.8HEALTHY0.6Hmemory<0.8DEGRADEDHmemory<0.6CRITICALH_{\text{memory}} \geq 0.8 \rightarrow \texttt{HEALTHY} \qquad 0.6 \leq H_{\text{memory}} < 0.8 \rightarrow \texttt{DEGRADED} \qquad H_{\text{memory}} < 0.6 \rightarrow \texttt{CRITICAL}

12.12.8 Continuous Evaluation Integration#

All quality metrics are computed continuously and integrated into the CI/CD pipeline:

  1. Pre-deployment: Run evaluation tasks against a snapshot of the memory store; block deployment if Hmemory<0.7H_{\text{memory}} < 0.7
  2. Post-deployment: Monitor all metrics for regression within the first hour; auto-rollback if MIER spikes above 2×2\times baseline
  3. Weekly audits: Full re-validation sweep of a statistically significant sample of durable memory; generate compliance reports
  4. Quarterly reviews: Human governance review of memory health trends, anti-pattern prevalence, and policy effectiveness

12.12.9 Metric Observability Architecture#

All metrics are emitted through structured telemetry:

MemoryMetricEvent {
  metric_name     : String         // e.g., "memory.write.acceptance_rate"
  dimensions      : Map<String, String>  // {layer, agent_id, topic_domain}
  value           : float64
  timestamp       : Timestamp
  window          : Duration       // Aggregation window
  source          : ServiceID      // Emitting service
}

These events flow to a time-series database (e.g., Prometheus, InfluxDB) with pre-configured dashboards and alerting rules. The observability layer is itself monitored for completeness—a metric that stops being emitted triggers a MISSING_METRIC\texttt{MISSING\_METRIC} alert.


Chapter Summary#

This chapter has defined the complete engineering discipline for governing writes to agentic memory systems. The key architectural invariants are:

  1. The write path is a gated, transactional pipeline with independent, typed validation stages and explicit accept/reject/defer semantics at each gate.
  2. Validation is three-layered: schema conformance ensures structural correctness, factual verification ensures empirical grounding, and contradiction detection ensures consistency with existing knowledge.
  3. Deduplication operates at three tiers (hash, structural fingerprint, semantic similarity) to prevent memory growth from exceeding knowledge growth.
  4. Provenance is immutable and tamper-evident, enabling trust calibration, blame attribution, and regulatory audit.
  5. Versioning is append-only, supporting point-in-time queries and safe rollback without data loss.
  6. Human-in-the-loop approval is risk-proportional, with automated risk classification determining whether writes auto-approve, queue for async review, or block for synchronous approval.
  7. Garbage collection combines TTL-based expiry, relevance decay modeling, and manual curation with two-phase deletion for safety.
  8. Cross-agent memory sharing is governed by RBAC + ABAC access control and lease-based write locks with fencing tokens.
  9. Consistency models are assigned per layer, balancing correctness against latency and availability.
  10. Regulatory compliance is mechanically enforced through erasure pipelines, residency constraints, and retention policies.
  11. Anti-patterns are identified and mitigated through structural mechanisms: rate limits, evidence diversity requirements, provenance cycle detection, and agent reputation scoring.
  12. Quality metrics are continuously computed across write-path, retrieval, downstream-impact, and operational dimensions, with a composite health score driving alerts and CI/CD gates.

The objective is not merely to store agent knowledge, but to maintain a trustworthy, auditable, self-healing epistemic substrate that improves agent performance over time while remaining governable, compliant, and resilient at production scale.