#1371·mempalace

kg: invalidate() should symmetrically reject ended < valid_from for the target row

Author: igorlsCreated May 6, 2026Updated Sep 16, 2026
Labelsbugarea/kg

Background

#1214 added an inverted-interval guard to `KnowledgeGraph.add_triple()` so a row with `valid_to < valid_from` cannot be persisted. This closes the foot-gun on the write path.

`KnowledgeGraph.invalidate()` (knowledge_graph.py:223 at the time of #1214) writes `valid_to` against an existing row without comparing the new `ended` value to that row's existing `valid_from`. A caller passing `ended="2020-01-01"` against a triple `valid_from="2026-01-01"` silently produces the same inverted state #1214 was preventing on the create path — invisible to every `query_entity` call, durable in SQLite forever.

Proposal

In `invalidate()`, look up the target row's `valid_from`, and reject the call with a clear `ValueError` if `ended < valid_from`. Mirrors the message format from `add_triple()`. The row may legitimately have multiple matches (subject/predicate/object can repeat across time); the guard should fire if any matched row would become inverted.

Acceptance

  • `invalidate(s, p, o, ended="2020-01-01")` against a triple `valid_from="2026-01-01"` raises ValueError naming both bounds.
  • `ended == valid_from` is allowed (point-in-time fact).
  • Open intervals (target row had no `valid_from`) pass through.
  • Test in `tests/test_knowledge_graph.py` exercising the rejection + non-regression for the legitimate paths.

Surfaced during review of #1214.