Skip to content

Contested & Dispositions

Every ingest_claim call returns exactly one disposition — a typed outcome describing what the engine did with the claim. There are 12 possible dispositions, covering the full space from clean commit to conflict to rejection. Disposition values are strings that match the Rust serde serialization names exactly.

In Python, the Disposition enum (from mempill.types) lets you compare against response dicts directly:

from mempill.types import Disposition
resp = engine.ingest_claim({ ... })
assert resp["disposition"] == Disposition.CommittedCheap

Contested is the correct outcome when two external claims contradict each other and no oracle is available to adjudicate. It is not an error state — it is an observable, auditable fact that the engine has received conflicting information and is surfacing it honestly.

Naive systems pick a winner silently. mempill refuses to do this (invariant I7, DC-4). Silently keeping the incumbent is not neutral — it is a decision to prefer old information over new information, made without evidence.

These are the exact values returned by the engine (source: mempill-python/python/mempill/types.py, Disposition enum):

Disposition Meaning
CommittedCheap New non-conflicting first-hand external fact; committed Active immediately via the cheap path
CommittedInferred ModelDerived claim; committed down-weighted; cannot overturn external beliefs
QueuedForAdjudication Belief-overturning conflict; oracle is present; accepted into async adjudication queue
Contested External contradiction; oracle absent or returned Unknown; no resolution; both values surfaced
PendingConflict Not enough evidence to overturn; awaiting additional evidence or oracle response
PendingReview A depended-on parent was superseded; this dependent is flagged for review
PendingLowConfidence Ambiguous source; awaiting corroboration before committing
Quarantined Burst/loop signature, or incoherent temporal bounds (B7 violation); parked but auditable
Superseded Belief-overturning accepted; prior claim’s valid-time window closed; new claim committed
Invalidated Explicit negative validity assertion; claim marked no-longer-true; retained in history
Reinstated Valid-time reopened by a first-hand assertion; previously bounded claim is live again
Rejected Missing/invalid provenance, malformed fact, write-authority violation, or temporal incoherence

TimingUncertain vs. Resolved (read-path statuses)

Section titled “TimingUncertain vs. Resolved (read-path statuses)”

These are read-path statuses on BeliefProjection, not write dispositions — but they are commonly confused:

  • Resolved — single live claim with valid_time_confidence >= 0.7. The engine has high confidence in both value and timing.
  • TimingUncertain — single live claim, but valid_time_confidence < 0.7 or no valid-time supplied. The value is the best available answer; timing is uncertain. This is the normal status for claims submitted without a valid-time window (see Quickstart).
Loading diagram…

When your agent receives a Contested disposition (or a query_memory returns status: "Contested"), the correct responses are:

  1. Acknowledge and surface it — inform the downstream consumer that the belief is contested. Do not guess a value. mempill’s demo agent renders both alternatives explicitly rather than picking one.
  2. Provide an oracle — configure an OraclePort using mempill.open_oracle(path, oracle). The oracle receives the conflict as an AdjudicationRequest and returns Affirm, Deny, or Unknown.
  3. Human-in-the-loop — use the HITL oracle pattern to route the conflict to a human reviewer via /review. See Human-in-the-Loop Oracle.
  4. Wait — for some predicates, a Contested state is acceptable indefinitely. The engine keeps both values safe and auditable.

See Oracle Resolution Loop and Human-in-the-Loop Oracle for resolution patterns.

result = engine.query_memory({
"agent_id": "my-agent",
"subject": "user",
"predicate": "city",
})
belief = result["belief"]
if belief["status"] == "Contested":
# primary is None — no winner was picked
# alternatives contains all contested values
for alt in belief["alternatives"]:
print(alt["fact"]["value"]) # e.g. "Berlin", "Munich"
elif belief["status"] in ("Resolved", "TimingUncertain"):
print(belief["primary"]["fact"]["value"])
elif belief["status"] == "NoBelief":
print("No belief found for this subject/predicate")