core / uncertainty.py — UncertaintyTracker

Metacognitive monitor that records unknowns and assumptions so the agent can decide when to ask the user vs. proceed.

Data shapes

TypeFields
Unknown (line 21)what · impact (1-5) · resolution · resolved · resolved_value · timestamp
Assumption (line 40)claim · confidence (0.0-1.0) · basis · verified · was_correct · timestamp

Methods

MethodPurpose
flag_unknown(what, impact=3, resolution="ask user") -> UnknownRecord an unknown the agent encountered. Returns the Unknown object so the caller can resolve it later without tracking indices.
resolve_unknown(index_or_unknown, value) -> boolMark an unknown resolved. Accepts EITHER an int index into self.unknowns OR the Unknown object returned by flag_unknown. Returns False on out-of-range int, orphan object, or unexpected types — never raises.
flag_assumption(claim, confidence=0.5, basis="") -> AssumptionRecord an unverified belief. Returns the Assumption.
verify_assumption(index_or_assumption, was_correct) -> boolSame dual-dispatch shape as resolve_unknown: int index or returned Assumption.
should_ask_user() -> Optional[str]Returns the question to ask if any unknown has impact ≥ 4 and resolution=="ask user".
get_risk_summary() -> strHuman-readable summary appended to the agent's final response.
get_context_for_prompt() -> strMarkdown for prompt injection (critical unknowns + risky assumptions).

Dual-dispatch API (object-or-index)

Before the fix, flag_unknown returned an Unknown but resolve_unknown only accepted an int. Holding the returned object and passing it back — the natural call shape — raised TypeError: '<=' not supported between instances of 'int' and 'Unknown'.

# Both now work, pick whichever is natural for the caller:
u = tracker.flag_unknown("timezone?", impact=4)
tracker.resolve_unknown(u, "UTC")            # object form (preferred)
tracker.resolve_unknown(0, "UTC")            # index form (backwards-compat)

a = tracker.flag_assumption("user wants python", confidence=0.4)
tracker.verify_assumption(a, was_correct=True)

Matching uses identity (is), NOT equality — two legitimately-distinct unknowns with the same what text are separate entries and must not both resolve on one call. Covered by tests/test_uncertainty_object_api.py (13 cases including orphan rejection, negative-index rejection, type-coercion rejection, duplicate-text identity check, mixed-mode usage).

Concurrency

Synchronous; per-process tracker (no persistence between sessions).