164 lines
5.0 KiB
Python
164 lines
5.0 KiB
Python
"""
|
|
Confidence Ladder — 5-level Verdict system.
|
|
|
|
Replaces binary pass/fail gates with a graduated confidence scale.
|
|
Each verdict carries a human-readable summary and optional evidence.
|
|
|
|
Levels:
|
|
PERFECT — All assertions passed, all paths verified
|
|
VERIFIED — Core assertions passed, edge cases known
|
|
PARTIAL — Nothing failed, but significant gaps were unverifiable
|
|
FEEDBACK — Assertions failed OR verification revealed blocking issues
|
|
FAILED — Assertions failed AND blocker will require rework
|
|
|
|
PARTIAL is the critical addition: it catches "green but dead" — cases where
|
|
the system ran without errors but couldn't prove correctness.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass, field, asdict
|
|
from datetime import datetime, timezone
|
|
from enum import Enum
|
|
from typing import Any, Optional
|
|
|
|
|
|
class VerdictLevel(Enum):
|
|
"""Five-level confidence ladder.
|
|
|
|
Ordered from most to least confident. Comparisons are meaningful:
|
|
``PERFECT > VERIFIED > PARTIAL > FEEDBACK > FAILED``.
|
|
"""
|
|
|
|
PERFECT = 5
|
|
VERIFIED = 4
|
|
PARTIAL = 3
|
|
FEEDBACK = 2
|
|
FAILED = 1
|
|
|
|
def __lt__(self, other: Any) -> bool:
|
|
if not isinstance(other, VerdictLevel):
|
|
return NotImplemented
|
|
return self.value < other.value
|
|
|
|
def __le__(self, other: Any) -> bool:
|
|
if not isinstance(other, VerdictLevel):
|
|
return NotImplemented
|
|
return self.value <= other.value
|
|
|
|
def __gt__(self, other: Any) -> bool:
|
|
if not isinstance(other, VerdictLevel):
|
|
return NotImplemented
|
|
return self.value > other.value
|
|
|
|
def __ge__(self, other: Any) -> bool:
|
|
if not isinstance(other, VerdictLevel):
|
|
return NotImplemented
|
|
return self.value >= other.value
|
|
|
|
@classmethod
|
|
def from_str(cls, label: str) -> VerdictLevel:
|
|
"""Parse a case-insensitive string to a VerdictLevel."""
|
|
return cls[label.upper()]
|
|
|
|
@property
|
|
def label(self) -> str:
|
|
return self.name
|
|
|
|
@property
|
|
def is_pass(self) -> bool:
|
|
"""``True`` for PERFECT, VERIFIED, PARTIAL — the system should continue."""
|
|
return self.value >= VerdictLevel.PARTIAL.value
|
|
|
|
@property
|
|
def is_blocking(self) -> bool:
|
|
"""``True`` for PERFECT, VERIFIED — may proceed without intervention."""
|
|
return self.value >= VerdictLevel.VERIFIED.value
|
|
|
|
|
|
@dataclass
|
|
class Verdict:
|
|
"""A single verdict from one verifier check."""
|
|
|
|
level: VerdictLevel
|
|
summary: str
|
|
evidence: list[str] = field(default_factory=list)
|
|
source: Optional[str] = None # e.g. "critic:style", "test:unit"
|
|
|
|
def dict(self) -> dict[str, Any]:
|
|
return {
|
|
"level": self.level.name,
|
|
"summary": self.summary,
|
|
"evidence": self.evidence,
|
|
"source": self.source,
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class VerdictReport:
|
|
"""Aggregate report from one or more verifier checks."""
|
|
|
|
verdicts: list[Verdict] = field(default_factory=list)
|
|
pipeline_id: Optional[str] = None # e.g. ADW ID or cron run ID
|
|
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
|
|
@property
|
|
def aggregate(self) -> VerdictLevel:
|
|
"""The lowest individual verdict sets the aggregate level.
|
|
|
|
The chain is only as strong as its weakest gate.
|
|
"""
|
|
if not self.verdicts:
|
|
return VerdictLevel.FAILED
|
|
return min(v.level for v in self.verdicts)
|
|
|
|
@property
|
|
def aggregate_label(self) -> str:
|
|
return self.aggregate.label
|
|
|
|
@property
|
|
def passed(self) -> bool:
|
|
"""``True`` if aggregate is PERFECT, VERIFIED, or PARTIAL."""
|
|
return self.aggregate.is_pass
|
|
|
|
@property
|
|
def blocked(self) -> bool:
|
|
"""``True`` if aggregate is FEEDBACK or FAILED."""
|
|
return not self.aggregate.is_pass
|
|
|
|
def add(self, verdict: Verdict) -> None:
|
|
self.verdicts.append(verdict)
|
|
|
|
def dict(self) -> dict[str, Any]:
|
|
return {
|
|
"pipeline_id": self.pipeline_id,
|
|
"timestamp": self.timestamp,
|
|
"aggregate": self.aggregate_label,
|
|
"passed": self.passed,
|
|
"blocked": self.blocked,
|
|
"verdicts": [v.dict() for v in self.verdicts],
|
|
}
|
|
|
|
def to_json(self, indent: int = 2) -> str:
|
|
return json.dumps(self.dict(), indent=indent)
|
|
|
|
@classmethod
|
|
def from_json(cls, raw: str) -> VerdictReport:
|
|
data = json.loads(raw)
|
|
report = cls(pipeline_id=data.get("pipeline_id"), timestamp=data.get("timestamp", ""))
|
|
for vd in data.get("verdicts", []):
|
|
report.add(
|
|
Verdict(
|
|
level=VerdictLevel.from_str(vd["level"]),
|
|
summary=vd["summary"],
|
|
evidence=vd.get("evidence", []),
|
|
source=vd.get("source"),
|
|
)
|
|
)
|
|
return report
|
|
|
|
def threshhold_met(self, minimum: VerdictLevel = VerdictLevel.PARTIAL) -> bool:
|
|
"""Check if aggregate meets a minimum confidence threshold."""
|
|
return self.aggregate >= minimum
|