Contested & Dispositions
Overview
Section titled “Overview”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.CommittedCheapWhy Contested is not an error
Section titled “Why Contested is not an error”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.
The 12 dispositions
Section titled “The 12 dispositions”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 withvalid_time_confidence >= 0.7. The engine has high confidence in both value and timing.TimingUncertain— single live claim, butvalid_time_confidence < 0.7or 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).
Disposition flow
Section titled “Disposition flow”Working with Contested claims
Section titled “Working with Contested claims”When your agent receives a Contested disposition (or a query_memory returns status: "Contested"), the correct responses are:
- 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.
- Provide an oracle — configure an
OraclePortusingmempill.open_oracle(path, oracle). The oracle receives the conflict as anAdjudicationRequestand returnsAffirm,Deny, orUnknown. - 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. - Wait — for some predicates, a
Contestedstate is acceptable indefinitely. The engine keeps both values safe and auditable.
See Oracle Resolution Loop and Human-in-the-Loop Oracle for resolution patterns.
Reading a contested belief
Section titled “Reading a contested belief”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")Next steps
Section titled “Next steps”- The Adjudication Gate — how the gate routes claims to these dispositions
- Oracle Resolution Loop — resolving
QueuedForAdjudication - Human-in-the-Loop Oracle — human review for
Contestedclaims - Valid-Time Succession — clean temporal chains that avoid
Contested