Skip to content

Temporal Consistency Graph

Track the structured claims a system makes over time and flag the moment it contradicts itself across sessions and documents — the "patient has diabetes on Monday, patient does not have diabetes on Tuesday" class of failure that matters for medical, legal, and financial workflows.

How it differs from cross-document memory

CrossDocumentConsistencyMemory compares whole documents by text similarity. The temporal consistency graph is complementary and operates on structured assertions on a timeline: a claim is a tuple (subject, predicate, value, polarity) recorded at a timestamp, in a session, from a document. That structure lets it detect exact, auditable contradictions rather than fuzzy textual overlap.

Contradiction kinds

Kind When
polarity The same (subject, predicate, value) is asserted, then negated (or vice versa)
functional_value A predicate declared single-valued is asserted with two different values (e.g. one diagnosis per patient)

Both are tenant-scoped: claims from different tenants never contradict each other.

Quick start

from director_ai import ProductionGuard
from director_ai.core.config import DirectorConfig
from director_ai.core.temporal_consistency import TemporalClaim

guard = ProductionGuard(DirectorConfig())
graph = guard.temporal_consistency  # persists across sessions on this guard

graph.record(TemporalClaim(
    subject="patient:123", predicate="has_condition", value="diabetes",
    polarity=True, timestamp=monday_ts, session_id="mon",
))

contradictions = graph.record(TemporalClaim(
    subject="patient:123", predicate="has_condition", value="diabetes",
    polarity=False, timestamp=tuesday_ts, session_id="tue",
))

assert contradictions[0].kind == "polarity"
assert contradictions[0].earlier.session_id == "mon"
assert contradictions[0].later.session_id == "tue"

record() returns the contradictions introduced by that claim (one per conflicting prior claim, ordered earlierlater by timestamp) and retains them for later querying.

Single-valued (functional) predicates

Declare predicates that may hold only one value per subject to catch silent value changes:

from director_ai.core.temporal_consistency import TemporalConsistencyGraph

graph = TemporalConsistencyGraph(functional_predicates={"diagnosis"})
graph.record(TemporalClaim("patient:1", "diagnosis", value="diabetes", timestamp=1.0))
flip = graph.record(TemporalClaim("patient:1", "diagnosis", value="healthy", timestamp=2.0))
assert flip[0].kind == "functional_value"

Querying and audit

graph.history("patient:123", "has_condition")     # claims oldest-first
graph.contradictions(tenant_id="acme")            # contradictions for a tenant
graph.report(tenant_id="acme")                     # tenant-safe summary dict

report() returns {tenant_id, subjects, claim_count, contradiction_count, contradictions, consistent}. Like every serialisation here it is tenant-safe — the raw source_text of a claim is omitted unless include_text=True is passed.

Tenant and privacy model

  • claims are grouped and compared only within (tenant_id, subject, predicate);
  • tenant ids are validated against ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$ (empty = the default tenant);
  • delete_tenant() removes a tenant's claims and contradictions for right-to-delete workflows;
  • comparison cost per recorded claim is linear in the size of its subject/predicate group.

The graph is in-memory and persists for the lifetime of the owning object; pair it with the provenance ledger when a durable, tamper-evident record is required.