# How MemPalace Handles Fact Invalidation in Its Temporal Knowledge Graph

> Learn how MemPalace handles fact invalidation by closing temporal intervals and preserving historical data in its SQLite knowledge graph. Explore its temporal approach.

- Repository: [MemPalace/mempalace](https://github.com/MemPalace/mempalace)
- Tags: internals
- Published: 2026-06-06

---

**MemPalace handles fact invalidation by closing the temporal interval on existing triples, setting the `valid_to` column to a specified end date while preserving the complete historical record in its SQLite-backed knowledge graph.**

MemPalace implements a temporal knowledge graph that tracks when facts become true and when they cease to be valid. Unlike deletion, **fact invalidation** preserves the assertion history by updating time-bound columns rather than removing rows, enabling point-in-time queries while maintaining data integrity through ACID-compliant SQLite operations.

## Temporal Data Model in the Triples Table

At the core of MemPalace's invalidation strategy is the `triples` table schema defined in [`mempalace/knowledge_graph.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/knowledge_graph.py). Each fact is stored as a temporal triple with `valid_from` and `valid_to` columns defined as TEXT fields containing ISO-8601 formatted timestamps.

When a fact is first asserted via `add_triple`, it enters the graph with `valid_to` set to `NULL`, indicating an active, currently valid assertion. The `valid_from` timestamp defaults to the insertion time, creating an open-ended interval that remains valid until explicitly closed through invalidation.

## The invalidate Method Implementation

The `KnowledgeGraph.invalidate()` method implements a three-step process to retire facts without deleting them. Located in [`mempalace/knowledge_graph.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/knowledge_graph.py) (lines 40-60), this method ensures atomic updates through SQLite's WAL mode and per-instance locking.

### Locating Active Facts

First, the method queries the `triples` table for rows matching the specified `subject`, `predicate`, and `object` where `valid_to IS NULL`. This SELECT operation at lines 40-44 identifies exactly the active fact instances that require invalidation, preventing accidental modification of already expired records.

### Temporal Validation

Before applying the invalidation, the method validates the supplied `ended` parameter (defaulting to the current date) using `sanitize_iso_temporal` from [`mempalace/config.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/config.py). The validation logic at lines 46-55 raises a `ValueError` if the end date precedes the fact's `valid_from` timestamp, protecting against inverted intervals where a fact would appear to end before it began.

### Marking Facts as Expired

Finally, the UPDATE statement at lines 57-60 sets `valid_to` to the normalized ISO timestamp. This operation closes the temporal interval while preserving the original assertion record. After this update, the fact remains in the database but is tagged as expired for all subsequent queries that filter on temporal boundaries.

## Thread Safety and Atomicity

MemPalace ensures safe concurrent invalidation through SQLite's Write-Ahead Logging (WAL) mode combined with explicit mutex locking. All write operations, including `invalidate`, acquire `self._lock` before executing SQL transactions.

This design prevents race conditions when multiple processes attempt to modify the same fact simultaneously. The WAL mode provides immediate persistence while allowing concurrent reads during write operations, ensuring that query methods never observe partially invalidated states.

## Query Behavior with Invalidated Facts

Once invalidated, facts are automatically excluded from temporal queries through the `_temporal_filter_sql` helper method. Both `query_entity` and `query_relationship` apply this filter when an `as_of` parameter is specified, comparing the query timestamp against the `valid_from` and `valid_to` boundaries.

Facts with `valid_to` set to a historical timestamp will only appear in queries where the `as_of` date falls within the valid interval. Current queries omit expired facts entirely, effectively treating them as logically deleted while retaining the audit trail for historical reconstruction.

## Practical Implementation Example

The following example demonstrates invalidating a fact when a condition no longer applies:

```python
from mempalace.knowledge_graph import KnowledgeGraph

# Initialize the knowledge graph

kg = KnowledgeGraph()

# Assert that Max has a sports injury starting January 1st, 2025

kg.add_triple(
    subject="Max",
    predicate="has_issue",
    obj="sports_injury",
    valid_from="2025-01-01"
)

# Verify the active fact exists

active_facts = kg.query_entity("Max")

# Returns the triple with valid_to: None

# Invalidate when the injury resolves on February 15th, 2026

kg.invalidate(
    subject="Max",
    predicate="has_issue",
    obj="sports_injury",
    ended="2026-02-15"
)

# The fact now shows valid_to: "2026-02-15T00:00:00Z"

# and will be excluded from current queries

```

This pattern maintains complete provenance: the database retains the full history of Max's injury from onset to resolution, while application logic treats the expired assertion as inactive for current-state queries.

## Summary

- MemPalace uses **temporal triples** with `valid_from` and `valid_to` columns in SQLite to track fact lifespans without deletion.
- The `invalidate` method in [`mempalace/knowledge_graph.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/knowledge_graph.py) performs an UPDATE operation on rows where `valid_to IS NULL`, preserving the original assertion data.
- **Input validation** in the `sanitize_iso_temporal` utility prevents inverted intervals where an end date precedes the start date.
- **Thread-safe locking** (`self._lock`) and SQLite WAL mode ensure atomic, race-condition-free invalidation operations.
- Temporal query filters automatically exclude invalidated facts from `query_entity` and `query_relationship` results when querying current states, while still allowing retrieval via historical `as_of` parameters.

## Frequently Asked Questions

### Does invalidation delete facts from the MemPalace knowledge graph?

No. Invalidation performs an UPDATE operation on the `triples` table, setting the `valid_to` timestamp while preserving the original row. This soft-delete approach maintains complete audit trails and enables historical point-in-time analysis without data loss.

### What happens if I attempt to invalidate a fact with an end date earlier than its start date?

The `invalidate` method raises a `ValueError` during the validation phase at lines 46-55 of [`knowledge_graph.py`](https://github.com/MemPalace/mempalace/blob/main/knowledge_graph.py). This check ensures temporal consistency by preventing the creation of invalid intervals where `valid_to` would be earlier than `valid_from`.

### How does MemPalace ensure thread safety during fact invalidation?

The implementation uses a per-instance lock (`self._lock`) acquired before all write operations, combined with SQLite's WAL (Write-Ahead Logging) mode. This prevents race conditions when multiple threads attempt to invalidate facts simultaneously while allowing concurrent read access to the knowledge graph.

### Are invalidated facts visible in entity queries?

Invalidated facts are automatically filtered out of current-state queries through the `_temporal_filter_sql` mechanism. However, they remain visible when querying with an explicit `as_of` timestamp that falls within the fact's valid interval, enabling temporal reconstruction of past knowledge states.