Oracle Resolution Loop
Overview
Section titled “Overview”The oracle resolution loop is the mechanism by which a Contested or QueuedForAdjudication claim moves to a resolved state. The OraclePort is a pluggable trait — you supply the implementation; mempill supplies the loop semantics, the durable pending store, and the resolution write path.
The oracle is the only mechanism that can flip a contested belief. No internal signal — corroboration count, recency, frequency — substitutes for oracle judgment (delivery constraint DC-4, invariant I7).
The two oracle states
Section titled “The two oracle states”Without an oracle configured, conflicts route to Contested (invariant I7). With an oracle configured (open_oracle / open_oracle_in_memory), conflicts route to QueuedForAdjudication instead — the engine calls request_adjudication() and stores the handle durably.
Pull model: request → submit
Section titled “Pull model: request → submit”The oracle loop operates in two halves:
Request side (at ingest time):
- A belief-overturning conflict is detected by the gate (C7).
oracle_present = true(oracle is configured) — gate routes to heavy path.- Engine calls
oracle.request_adjudication(agent_id, request). AdjudicationRequestcarries:{subject_line, incumbent, challenger, criticality, reason}.- Oracle returns an opaque
handle_id(UUID) immediately. It does not block. - Engine stores
handle_id → (agent_id, challenger_claim_ref, incumbent_claim_ref)in the durablepending_adjudicationstable (survives restart). ingest_claimreturnsdisposition: QueuedForAdjudication.
Submit side (when oracle has a verdict):
- Host calls
engine.submit_adjudication(response). AdjudicationResponsecarries:{handle_id, verdict, evidence_provenance}.- Engine looks up
handle_idin the pending store. - Engine acquires the per-agent write lock (I9, DC-2).
- Engine applies the verdict atomically (I9).
- Engine removes
handle_idfrom the pending store (idempotency guard). - Returns
AdjudicationOutcome {handle_id, disposition, claim_ref}.
Verdict semantics
Section titled “Verdict semantics”| Verdict | Disposition after submit_adjudication |
What the engine writes |
|---|---|---|
Affirm |
Challenger → CommittedCheap (or Reinstated if reopening a bounded claim). Incumbent → Superseded. |
Bounding ValidityAssertion on incumbent + ledger entry for both. Resolution carries External(ExternalFirstHand) provenance. |
Deny |
Incumbent stays active. Challenger → Superseded. |
Bounding ValidityAssertion on challenger + ledger entry. |
Unknown |
No disposition flip. Both claims remain Contested. Handle consumed. |
Ledger entry recording the abstain. |
Sequence diagram
Section titled “Sequence diagram”Host (ingest) EngineHandle Oracle (host impl) Host (submit) │ │ │ │ │──ingest_claim()─►│ │ │ │ │ conflict detected │ │ │ │ oracle_present=true │ │ │ │──request_adj(req)───►│ │ │ │◄─handle_id:Uuid──────│ │ │ │ (stored in DB) │ │ │◄─QueuedForAdj────│ │ │ │ │ │ │ │ [async gap — milliseconds to days] │ │ │ │ │ │ │ │ Oracle decides verdict │ │ │ │ │ │ │◄─────────────────────│─submit_adj(resp)───│ │ │ acquire write lock │ │ │ │ apply verdict (I9) │ │ │ │ write ledger+bound │ │ │ │ remove handle │ │ │ │──────────────────────│──AdjudicationOutcome►│Durable pending store
Section titled “Durable pending store”The pending_adjudications table is the source of truth for outstanding oracle requests. It persists across engine restarts. This means:
- If the process crashes between
request_adjudicationandsubmit_adjudication, the pending row is still in the database. - On restart,
list_pending_adjudications()returns all outstanding items. The oracle implementation can reinspect them and re-queue if needed. - Claims stay
QueuedForAdjudicationin the ledger until a verdict arrives.
Engine-enforced TTL and sweep
Section titled “Engine-enforced TTL and sweep”The engine supports a configurable default_adjudication_ttl (optional; None = indefinite). When a handle expires:
- The engine sets the pending row’s status to
expired. sweep_expired_adjudications()reverts all expired-but-unresolved claims toContestedand writes ledger entries for each.- The host is responsible for calling
sweep_expired_adjudications()periodically (or at startup) — the engine does not run a background sweep.
# Sweep on startup to revert any claims whose TTL elapsed while offline.n = engine.sweep_expired_adjudications()if n > 0: print(f"[startup] Reverted {n} expired adjudication(s) to Contested.")Configuring an oracle in Python
Section titled “Configuring an oracle in Python”import mempill
class MyOracle: """Minimal oracle: always abstain.""" def request_adjudication(self, agent_id: str, request: dict) -> str: import uuid # In a real oracle: store (handle_id → request) in your queue; return handle_id. # The engine stores the full request in pending_adjudications; you just need the ID. return str(uuid.uuid4())
oracle = MyOracle()engine = mempill.open_oracle("/path/to/agent.db", oracle)
# Later, when you have a verdict:outcome = engine.submit_adjudication({ "handle_id": handle_id, "verdict": "Affirm", # or "Deny" or "Unknown" "evidence_provenance": {"type": "External", "kind": "ExternalFirstHand"},})print(outcome["disposition"]) # CommittedCheap (challenger won)Listing pending adjudications
Section titled “Listing pending adjudications”# OracleEngine exposes list_pending_adjudications().pending = engine.list_pending_adjudications(agent_id="my-agent")for item in pending: print(item["handle_id"], item["predicate"], item["incumbent_value"], item["challenger_value"])What happens if the oracle never responds?
Section titled “What happens if the oracle never responds?”- With no TTL configured: claims stay
QueuedForAdjudicationindefinitely. They are safe and auditable; they do not affect other beliefs. - With a TTL configured:
sweep_expired_adjudications()reverts them toContestedafter the TTL elapses. - In either case, the engine never silently picks a winner. Absence of oracle response =
Contested, not incumbent-wins.
Key invariants
Section titled “Key invariants”- I7 — Contested first-class: oracle absent or
Unknown→Contested, never silent incumbent-wins. - I9 — Atomic commit unit:
submit_adjudicationapplies verdict atomically (bounding assertion + disposition flip + ledger entry in one transaction). - DC-4 — Oracle is the only correctness pressure: no internal signal substitutes for oracle judgment.
- I4 — Provenance immutable: oracle-resolved claims carry
External(ExternalFirstHand)provenance, written at resolution time and immutable thereafter.
Next steps
Section titled “Next steps”- Human-in-the-Loop Oracle — routing conflicts to a human reviewer
- Contested and Dispositions — the full 12-state model
- The Adjudication Gate — how claims are routed before the oracle is called