fix(kg): reject inverted intervals in add_triple (valid_to < valid_from)#1214
fix(kg): reject inverted intervals in add_triple (valid_to < valid_from)#1214arnoldwender wants to merge 1 commit intoMemPalace:developfrom
Conversation
A triple with valid_to < valid_from satisfies neither of the temporal
filter clauses in query_entity():
valid_from <= as_of AND valid_to >= as_of
so the triple is invisible to every query — silently corrupt. Reject
at write time with a clear error instead of letting bad data pile up
in the SQLite store.
The guard only fires when both bounds are present; open intervals
(only valid_from or only valid_to) are still accepted, and same-day
intervals (valid_from == valid_to, point-in-time facts) are explicitly
allowed.
|
+1 to rejecting at write time — silently invisible triples are a hard failure mode to debug after the fact. One coordination note worth surfacing: lex-string "2026-12-01" < "2026-3-01" # True — the guard would falsely reject Dec→Mar as invertedThe PR description names the dependency on #1167. Just noting that if #1167 stalls and #1214 lands first, MCP Low-risk in practice (LLM-driven callers tend to emit ISO 8601), and the right fix shape is #1167 anyway. Not blocking — just worth tagging the dependency in case the merge order goes #1214 → #1167 rather than the other way. |
|
@jphein Thanks — good call on tagging the merge-order gap explicitly. Concrete behavior if #1214 lands before #1167:
Happy with either merge order. If #1167 first, the gap closes naturally; if #1214 first, the limitation is documented and #1167 follows up. |
What and Why
A triple with
valid_tobeforevalid_fromsatisfies neither of the temporal filter clauses inKnowledgeGraph.query_entity():so the triple is invisible to every query — silently corrupt. The data lives in SQLite forever but never surfaces, and the caller never sees a warning. This is a P0 data-integrity bug in a path adapters can hit easily (any caller that mixes up the two date params).
Root Cause
mempalace/knowledge_graph.py:149—add_triple()acceptsvalid_fromandvalid_toas opaque strings, validates each format independently in upstream callers (and now PR #1167 at the MCP boundary), but never checks the relationship between them.Fix
Reject at write time with a clear ValueError naming both bounds. The guard fires only when both are set:
valid_from, or onlyvalid_to) — accepted unchanged.valid_from == valid_to, point-in-time facts) — explicitly allowed (<not<=).sanitize_iso_date(PR fix(kg): validate ISO-8601 date formats at MCP boundary #1167) ensuresYYYY-MM-DDlex order matches calendar order.Test plan
test_add_triple_rejects_inverted_interval— asserts ValueError with'before valid_from'matchtest_add_triple_accepts_equal_dates— point-in-time facts passtest_add_triple_allows_only_one_bound— open intervals (one bound) still passtest_knowledge_graph.pytests greentest_backends.py::test_pin_hnsw_threads*unrelated — environmental ChromaDBconfiguration_json["hnsw"]schema drift, also fails on a cleandevelopcheckout)