ADR-0175 Phase 1 — standalone, deterministic, zero serving change. Nothing in
the serving/eval path imports it.
core/reliability_gate/:
- floor.py: conservative_floor(s,k) — pinned one-sided Wilson lower bound over
COMMITTED trials. z=2.576, N_MIN=10; range [0,1) (never exactly 1.0); float64
rounded half-to-even to 1e-9 for cross-backend replay. Perfect record reduces
to k/(k+z²) (earned by volume).
- ledger.py: ClassTally — immutable per-class counts; reliability = commitment
precision (refusals excluded so coverage never penalizes reliability);
t2_precision over the anchor set; coverage tracked separately.
- ceilings.py: Action{PRACTICE,PROPOSE,SERVE} + Ceilings — human-set θ
(practice=0, propose=.85, serve=.99). Frozen; with_override returns a NEW
instance (no in-place self-authorization).
- gate.py: license_for() — deterministic gate, measured/required≥1 (≡ measured≥
required; required=0 ⟹ always). Pure; never mutates/emits ceilings.
34 tests, each ADR invariant exercised by a test that fails under its violation:
#3 determinism/replay (idempotent, pre-rounded, deterministic decisions),
#4 no self-authorization (frozen ceilings; gate never emits/mutates them),
#1 proxy (zero serving coupling). Plus the §4a worked examples (38 clean
commitments clear propose; one wrong in 40 drops below; serve needs ~657).
Verified: 34/34 pass; architectural invariants 40/40; smoke 67/67; ruff clean;
no serving/eval import of the package.
91 lines
3 KiB
Python
91 lines
3 KiB
Python
"""ADR-0175 §4 — the per-class calibration ledger.
|
|
|
|
A replayable tally of *counted* outcomes per class (= capability axis: G1..G5,
|
|
S1, ...). Nothing learned, nothing stochastic — every figure is an integer count.
|
|
|
|
Reliability is **commitment precision** (``correct / (correct + wrong)`` via the
|
|
pinned :func:`conservative_floor`): refusals are excluded from the denominator on
|
|
purpose. Refusing is always safe, so a high refusal rate is a *coverage* fact
|
|
(``coverage``), never a *reliability* penalty.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from core.reliability_gate.floor import conservative_floor
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ClassTally:
|
|
"""Immutable per-class outcome counts.
|
|
|
|
``class_name`` is a capability-axis id. All mutation is via :meth:`record`,
|
|
which returns a new tally (immutability rule).
|
|
"""
|
|
|
|
class_name: str
|
|
correct: int = 0
|
|
wrong: int = 0
|
|
refused: int = 0
|
|
t2_verified: int = 0
|
|
t2_agrees_gold: int = 0
|
|
|
|
def __post_init__(self) -> None:
|
|
for value in (
|
|
self.correct,
|
|
self.wrong,
|
|
self.refused,
|
|
self.t2_verified,
|
|
self.t2_agrees_gold,
|
|
):
|
|
if not isinstance(value, int) or value < 0:
|
|
raise ValueError("tally counts must be non-negative ints")
|
|
if self.t2_agrees_gold > self.t2_verified:
|
|
raise ValueError(
|
|
f"t2_agrees_gold ({self.t2_agrees_gold}) cannot exceed "
|
|
f"t2_verified ({self.t2_verified})"
|
|
)
|
|
|
|
@property
|
|
def committed(self) -> int:
|
|
"""Attempts where the engine committed to an answer (excludes refusals)."""
|
|
return self.correct + self.wrong
|
|
|
|
@property
|
|
def attempted(self) -> int:
|
|
return self.correct + self.wrong + self.refused
|
|
|
|
@property
|
|
def reliability(self) -> float:
|
|
"""Conservative lower bound on commitment precision (ADR-0175 §4/§4a)."""
|
|
return conservative_floor(self.correct, self.committed)
|
|
|
|
@property
|
|
def t2_precision(self) -> float:
|
|
"""How trustworthy Tier-2 self-verification is on this class (vs gold)."""
|
|
return conservative_floor(self.t2_agrees_gold, self.t2_verified)
|
|
|
|
@property
|
|
def coverage(self) -> float:
|
|
"""Commit rate = committed / attempted. A coverage fact, not reliability."""
|
|
return round(self.committed / self.attempted, 9) if self.attempted else 0.0
|
|
|
|
def record(
|
|
self,
|
|
*,
|
|
correct: int = 0,
|
|
wrong: int = 0,
|
|
refused: int = 0,
|
|
t2_verified: int = 0,
|
|
t2_agrees_gold: int = 0,
|
|
) -> "ClassTally":
|
|
"""Return a new tally with the given outcomes added (immutable update)."""
|
|
return ClassTally(
|
|
class_name=self.class_name,
|
|
correct=self.correct + correct,
|
|
wrong=self.wrong + wrong,
|
|
refused=self.refused + refused,
|
|
t2_verified=self.t2_verified + t2_verified,
|
|
t2_agrees_gold=self.t2_agrees_gold + t2_agrees_gold,
|
|
)
|