test-driver/src/testdriver/oracles.py
tegwick 1b9860a8ee T10: gate review and first compression pass
All four gate criteria met. False Adaptation Rate 0/7 with 12 of 13 mechanical
mutations absorbed. 178 tests pass. TD-WP-0002 finished.

Fitness loop closed via F-0003: actor isolation was a property of scenarios
written to expose it, not of runs. Actors now carry an automatic private
marker and the runner examines all of them on every scenario, with two
permanent regressions behind it.

Compression - six abstractions removed, each declared and never used:
Verdict.SUSPICIOUS (a verdict no oracle could emit), Step.expect_refusal,
ActorIsolationError, World.seed, EvidencePack.latest, Trajectory.method.

F-0008: Temperature may be redundant. Crystallization was built without it
ever being consulted; measured stability of realization did the work, and is
observed rather than declared. Gated for removal alongside energy.py.

INTENT_CHANGED and REALIZATION_FAILED had never run. Both now have
purpose-built cases and a test that fails if a seventh outcome is added
without one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1629012@bnt-lap001
Assistant-Session: 78d4fb13-8a1e-474b-87a3-9b9261c49a39
2026-08-23 00:39:36 +02:00

107 lines
3.3 KiB
Python

"""Oracles evaluate claims and invariants and produce verdicts.
An oracle reads only the independent observation snapshot. It has no access to
the actor, to the driver, or to what either of them believes happened.
`SUSPICIOUS` was removed at T10: no oracle could produce it, and a verdict
nothing can emit is a promise the framework does not keep. It returns, with an
identifier, if a mechanism ever needs it.
`INCONCLUSIVE` is a first-class outcome, not a failure mode of the framework. An
oracle that cannot support a judgment from the retained evidence must say so
rather than defaulting either way — silently defaulting to PASS hides defects,
and silently defaulting to FAIL trains people to ignore results.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Mapping
from .intent import Claim, Invariant
class Verdict(str, Enum):
PASS = "PASS"
FAIL = "FAIL"
INCONCLUSIVE = "INCONCLUSIVE"
@dataclass(frozen=True, slots=True)
class Judgment:
assertion_id: str
text: str
verdict: Verdict
step_id: str | None
detail: dict[str, Any] = field(default_factory=dict)
def as_dict(self) -> dict[str, Any]:
return {
"assertion_id": self.assertion_id,
"text": self.text,
"verdict": self.verdict.value,
"step_id": self.step_id,
"detail": self.detail,
}
class Oracle:
"""Deterministic evaluation of one assertion against an observation snapshot."""
def judge(
self,
assertion: Claim | Invariant,
snapshot: Mapping[str, Any],
step_id: str | None,
) -> Judgment:
if not snapshot:
return Judgment(
assertion.id,
assertion.text,
Verdict.INCONCLUSIVE,
step_id,
{"reason": "no observations were collected"},
)
try:
satisfied = assertion.predicate(snapshot)
except KeyError as missing:
# The evidence needed to judge this assertion was not collected.
# That is an evidence failure, never a pass and never a fail.
return Judgment(
assertion.id,
assertion.text,
Verdict.INCONCLUSIVE,
step_id,
{"reason": f"required observation {missing} missing from snapshot"},
)
except Exception as exc:
return Judgment(
assertion.id,
assertion.text,
Verdict.INCONCLUSIVE,
step_id,
{"reason": f"predicate raised {type(exc).__name__}: {exc}"},
)
return Judgment(
assertion.id,
assertion.text,
Verdict.PASS if satisfied else Verdict.FAIL,
step_id,
)
def overall(judgments: list[Judgment]) -> Verdict:
"""Aggregate run verdict.
FAIL dominates; INCONCLUSIVE outranks PASS. A run containing an unjudgeable
assertion has not passed, whatever else it did.
"""
verdicts = {j.verdict for j in judgments}
if not judgments:
return Verdict.INCONCLUSIVE
if Verdict.FAIL in verdicts:
return Verdict.FAIL
if Verdict.INCONCLUSIVE in verdicts:
return Verdict.INCONCLUSIVE
return Verdict.PASS