refactor(learning): extract the ratified-ledger bridge from its three instances (ADR-0263)

Phase 3.3 of the generalization arc. Estimation (ADR-0175), deduction
serving (ADR-0256) and curriculum serving (ADR-0262) had each written the
same seal -> ratify -> SHA-verify -> serve-gate machinery. core/ratified_ledger.py
now owns it and states the four rules once: only sealed practice writes;
tamper-evidence is structural (a load that cannot reproduce content_sha256
REFUSES); ceilings are not negotiable at the call site; absent evidence is
never a license.

Each capability keeps a thin adapter that names its artifact and preserves
its public API. One real difference is now declared rather than implied:
missing_ok distinguishes a ledger a capability SHIPS with (absence = broken
deployment, refuse) from one whose practice volume is still being built
(absence = nothing earned yet, serve disclosed) — curriculum serving is the
second kind today.

Safety property is byte-identity, asserted not assumed: re-sealing the
committed 25-band deduction ledger through the bridge reproduces it
byte-for-byte, so no artifact and no lane pin moves.

Effect: a new subject arena now needs a gold corpus and a band key, not a
re-implementation of ratification — which is what §3 meant by sequencing
the bridge ahead of the second subject.

[Verification]: tests/test_ratified_ledger_bridge.py 8 passed; core test
--suite deductive 252 passed; estimation/license test set 355 passed;
committed deduction ledger byte-identical after reseal.
This commit is contained in:
Shay 2026-07-24 14:45:16 -07:00
parent 44e78aa438
commit 0a17c49693
8 changed files with 409 additions and 146 deletions

View file

@ -10,63 +10,40 @@ never writes it — the sealed-practice output of
its ``content_sha256`` is verified on load so a hand-edited ledger is rejected
rather than silently trusted.
A deliberate near-copy of ``chat.deduction_serve_license``: the second
instance of the seal ratify SHA-verify serve-gate pattern in this arc,
kept explicit here so the shared bridge (generalization plan Phase 3.3) is
extracted from two working instances rather than designed from one.
The third instance of the seal ratify SHA-verify serve-gate pattern, and
the one that made the shared bridge worth extracting: this module is now an
ADAPTER over ``core.ratified_ledger`` (ADR-0263). It names the artifact, keeps
the memoization, and declares the one thing that genuinely differs this
ledger is legitimately ABSENT, because no curriculum band has earned anything
yet (ADR-0262 §5.1: the binding constraint is ratified curriculum volume). An
absent ledger reads as an empty table, so every answer is served DISCLOSED.
"""
from __future__ import annotations
import json
from functools import lru_cache
from pathlib import Path
from core.reliability_gate import Action, Ceilings, ClassTally, LicenseDecision, license_for
from formation.hashing import sha256_of
from core.ratified_ledger import (
RatifiedLedgerError as RatifiedCurriculumLedgerError,
load_sealed_ledger,
serve_license,
)
from core.reliability_gate import Ceilings, ClassTally, LicenseDecision
_LEDGER_PATH = Path(__file__).resolve().parent / "data" / "curriculum_serve_ledger.json"
class RatifiedCurriculumLedgerError(ValueError):
"""The committed curriculum-serve ledger is missing, malformed, or tampered with."""
@lru_cache(maxsize=1)
def load_ratified_ledger() -> dict[str, ClassTally]:
"""Load + verify the ratified curriculum-serve ledger → per-band tallies.
An ABSENT ledger is not an error: it means no curriculum band has earned
anything yet, and every answer is served DISCLOSED. That is the honest
default for a capability whose practice volume is still being built, and
it keeps the composer usable before any license exists.
An ABSENT ledger is not an error here: no curriculum band has earned
anything yet, and the honest reading of "no file" is "no committed
evidence", which the gate turns into a disclosed answer rather than a
withheld one.
"""
if not _LEDGER_PATH.exists():
return {}
try:
artifact = json.loads(_LEDGER_PATH.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc: # pragma: no cover - defensive
raise RatifiedCurriculumLedgerError(f"cannot read ratified ledger: {exc}") from exc
classes = artifact.get("classes")
if not isinstance(classes, dict):
raise RatifiedCurriculumLedgerError("ratified ledger has no 'classes' table")
if sha256_of(classes) != artifact.get("content_sha256"):
raise RatifiedCurriculumLedgerError(
"ratified ledger content_sha256 mismatch — not the sealed-practice output"
)
ledger: dict[str, ClassTally] = {}
for cls, counts in classes.items():
ledger[cls] = ClassTally(
class_name=cls,
correct=int(counts.get("correct", 0)),
wrong=int(counts.get("wrong", 0)),
refused=int(counts.get("refused", 0)),
t2_verified=int(counts.get("t2_verified", 0)),
t2_agrees_gold=int(counts.get("t2_agrees_gold", 0)),
)
return ledger
return load_sealed_ledger(_LEDGER_PATH, missing_ok=True)
def curriculum_serve_license(
@ -81,11 +58,7 @@ def curriculum_serve_license(
caller serves a disclosed (hedged) surface, the safe default.
"""
ledger = ledger if ledger is not None else load_ratified_ledger()
tally = ledger.get(band)
if tally is None:
return None
ceilings = ceilings if ceilings is not None else Ceilings.default()
return license_for(tally, Action.SERVE, ceilings)
return serve_license(band, ledger, ceilings=ceilings)
__all__ = [

View file

@ -10,27 +10,27 @@ artifact is the sealed-practice output of
is verified on load, so a hand-edited (un-ratified) ledger is rejected rather
than silently trusted.
Mirrors ``generate.determine.estimation_license`` exactly: immutable ratified
data parsed once and cached; the gate (``license_for``) is pure; ceilings stay
at the safe defaults (invariant #4 — the engine cannot raise its own bar).
The load/verify/gate mechanics live in ``core.ratified_ledger`` (ADR-0263)
this module is the deduction-serve ADAPTER over that bridge: it names the
artifact, keeps the memoization, and preserves its own public API. Ceilings
stay at the safe defaults (invariant #4 — the engine cannot raise its own bar).
"""
from __future__ import annotations
import json
from functools import lru_cache
from pathlib import Path
from core.reliability_gate import Action, Ceilings, ClassTally, LicenseDecision, license_for
from formation.hashing import sha256_of
from core.ratified_ledger import (
RatifiedLedgerError,
load_sealed_ledger,
serve_license,
)
from core.reliability_gate import Ceilings, ClassTally, LicenseDecision
_LEDGER_PATH = Path(__file__).resolve().parent / "data" / "deduction_serve_ledger.json"
class RatifiedLedgerError(ValueError):
"""The committed deduction-serve ledger is missing, malformed, or tampered with."""
@lru_cache(maxsize=1)
def load_ratified_ledger() -> dict[str, ClassTally]:
"""Load + verify the ratified deduction-serve ledger → per-band ``ClassTally``.
@ -39,30 +39,7 @@ def load_ratified_ledger() -> dict[str, ClassTally]:
recomputed ``content_sha256`` does not match the committed one (tamper-evidence:
only the sealed-practice output is trusted, never a hand-edited ledger).
"""
try:
artifact = json.loads(_LEDGER_PATH.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc: # pragma: no cover - defensive
raise RatifiedLedgerError(f"cannot read ratified ledger: {exc}") from exc
classes = artifact.get("classes")
if not isinstance(classes, dict):
raise RatifiedLedgerError("ratified ledger has no 'classes' table")
if sha256_of(classes) != artifact.get("content_sha256"):
raise RatifiedLedgerError(
"ratified ledger content_sha256 mismatch — not the sealed-practice output"
)
ledger: dict[str, ClassTally] = {}
for cls, counts in classes.items():
ledger[cls] = ClassTally(
class_name=cls,
correct=int(counts.get("correct", 0)),
wrong=int(counts.get("wrong", 0)),
refused=int(counts.get("refused", 0)),
t2_verified=int(counts.get("t2_verified", 0)),
t2_agrees_gold=int(counts.get("t2_agrees_gold", 0)),
)
return ledger
return load_sealed_ledger(_LEDGER_PATH)
def deduction_serve_license(
@ -79,11 +56,7 @@ def deduction_serve_license(
safe default ceilings.
"""
ledger = ledger if ledger is not None else load_ratified_ledger()
tally = ledger.get(shape_band)
if tally is None:
return None
ceilings = ceilings if ceilings is not None else Ceilings.default()
return license_for(tally, Action.SERVE, ceilings)
return serve_license(shape_band, ledger, ceilings=ceilings)
__all__ = ["RatifiedLedgerError", "deduction_serve_license", "load_ratified_ledger"]

View file

@ -184,6 +184,7 @@ TEST_SUITES: dict[str, tuple[str, ...]] = {
"tests/test_exist_argument_reader.py",
"tests/test_deduction_serve_e2e.py",
"tests/test_curriculum_serve.py",
"tests/test_ratified_ledger_bridge.py",
),
"full": ("tests/",),
}

157
core/ratified_ledger.py Normal file
View file

@ -0,0 +1,157 @@
"""The ratified-ledger bridge — seal → ratify → SHA-verify → serve-gate.
ADR-0175 Phase 5's consumption bridge (generalization plan Phase 3.3),
extracted from three working instances rather than designed ahead of them:
- ``generate/determine/estimation_license.py`` (ADR-0175, the first)
- ``chat/deduction_serve_license.py`` (ADR-0256, the second)
- ``chat/curriculum_serve_license.py`` (ADR-0262, the third)
All three had converged on the same artifact and the same four rules, which is
what makes this an extraction and not a speculation. The rules, stated once:
1. **The engine reads; only sealed practice writes.** A ledger is the output
of a practice run over a gold corpus, never of a serving turn. Nothing in a
serving path may call :func:`write_sealed_ledger`.
2. **Tamper-evidence is structural.** The artifact carries
``content_sha256`` over its ``classes`` table; a load that cannot reproduce
it REFUSES. A hand-edited ledger is not a slightly-wrong ledger, it is an
unratified one.
3. **Ceilings are not negotiable at the call site.** The gate always runs at
the safe defaults unless a caller passes ceilings explicitly, and no
production path does ADR-0175 invariant #4: an engine cannot raise its own
bar.
4. **Absent evidence is never a license.** A class missing from the ledger
yields ``None``, and every caller's ``None`` branch serves the disclosed
(hedged) surface. A capability with no track record is served honestly, not
withheld and not asserted.
Byte-compatibility is deliberate: :func:`seal_artifact` and
:func:`write_sealed_ledger` reproduce the exact bytes the three existing
sealers wrote, so adopting the bridge re-seals every committed ledger
identically and no lane pin moves.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from core.reliability_gate import (
Action,
Ceilings,
ClassTally,
LicenseDecision,
license_for,
)
from formation.hashing import sha256_of
class RatifiedLedgerError(ValueError):
"""A committed ledger is malformed or does not verify against its own hash."""
def tally_dict(tally: ClassTally) -> dict[str, Any]:
"""The committed per-class row. The field set is the contract — a reader
of an older ledger must be able to name every field it finds."""
return {
"correct": tally.correct,
"wrong": tally.wrong,
"refused": tally.refused,
"t2_verified": tally.t2_verified,
"t2_agrees_gold": tally.t2_agrees_gold,
}
def seal_artifact(
ledger: dict[str, ClassTally], *, schema: str, note: str, provenance: str
) -> dict[str, Any]:
"""The self-verifying sealed-ledger dict for *ledger*.
Classes are sorted, so the artifact is a pure function of the practice
result: the same corpus and solver seal byte-identically, which is what
makes a committed ledger reviewable as a diff.
"""
classes = {name: tally_dict(tally) for name, tally in sorted(ledger.items())}
return {
"schema": schema,
"classes": classes,
"content_sha256": sha256_of(classes),
"note": note,
"provenance": provenance,
}
def write_sealed_ledger(path: Path, artifact: dict[str, Any]) -> dict[str, Any]:
"""Write *artifact* to *path* in the committed formatting."""
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(artifact, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
return artifact
def load_sealed_ledger(path: Path, *, missing_ok: bool = False) -> dict[str, ClassTally]:
"""Load + verify a sealed ledger → per-class ``ClassTally``.
``missing_ok`` distinguishes two genuinely different situations. A ledger
that a capability *ships with* is required: its absence means the
deployment is broken, and refusing is right. A ledger for a capability
whose practice volume is still being built is legitimately absent, and the
honest reading of "no file" is "no class has earned anything yet" an
empty table, every answer disclosed. Neither case may be answered by
guessing a license.
"""
if not path.exists():
if missing_ok:
return {}
raise RatifiedLedgerError(f"ratified ledger not found: {path}")
try:
artifact = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise RatifiedLedgerError(f"cannot read ratified ledger: {exc}") from exc
classes = artifact.get("classes") if isinstance(artifact, dict) else None
if not isinstance(classes, dict):
raise RatifiedLedgerError("ratified ledger has no 'classes' table")
if sha256_of(classes) != artifact.get("content_sha256"):
raise RatifiedLedgerError(
"ratified ledger content_sha256 mismatch — not the sealed-practice output"
)
return {
name: ClassTally(
class_name=name,
correct=int(counts.get("correct", 0)),
wrong=int(counts.get("wrong", 0)),
refused=int(counts.get("refused", 0)),
t2_verified=int(counts.get("t2_verified", 0)),
t2_agrees_gold=int(counts.get("t2_agrees_gold", 0)),
)
for name, counts in classes.items()
}
def serve_license(
class_name: str,
ledger: dict[str, ClassTally],
*,
ceilings: Ceilings | None = None,
) -> LicenseDecision | None:
"""The ``Action.SERVE`` verdict for *class_name*, or ``None`` when the
class has no committed evidence (never a license rule 4)."""
tally = ledger.get(class_name)
if tally is None:
return None
return license_for(tally, Action.SERVE, ceilings or Ceilings.default())
__all__ = [
"RatifiedLedgerError",
"load_sealed_ledger",
"seal_artifact",
"serve_license",
"tally_dict",
"write_sealed_ledger",
]

View file

@ -0,0 +1,80 @@
# ADR-0263 — The ratified-ledger bridge
- **Status:** Proposed
- **Date:** 2026-07-24
- **Arc:** generalization Phase 3.3 (docs/plans/generalization-arc-2026-07-24.md §2)
- **Governs:** `core/ratified_ledger.py` and the three adapters over it —
`generate/determine/estimation_license.py`, `chat/deduction_serve_license.py`,
`chat/curriculum_serve_license.py` — plus the sealed-artifact writer in
`evals/deduction_serve/practice/runner.py`.
## 1. Context
Three capabilities had independently converged on the same four-step pattern:
sealed practice writes a per-class tally table → the table is committed as a
ratified artifact → serving verifies its `content_sha256` on load → a per-class
gate decides SERVE against fixed ceilings.
Three near-identical implementations is the point at which a shared component
is an extraction rather than a guess. It matters now because the generalization
plan puts a per-subject arena behind every new subject: without a bridge, each
subject copies fifty lines of verification, and the copies drift.
## 2. Decision
`core/ratified_ledger.py` owns the mechanics — `seal_artifact`,
`write_sealed_ledger`, `load_sealed_ledger`, `serve_license`, `tally_dict`
and states the four rules once:
1. **The engine reads; only sealed practice writes.**
2. **Tamper-evidence is structural** — a load that cannot reproduce
`content_sha256` REFUSES. A hand-edited ledger is not a slightly-wrong
ledger; it is an unratified one.
3. **Ceilings are not negotiable at the call site** (ADR-0175 invariant #4).
4. **Absent evidence is never a license** — a missing class yields `None`, and
every caller's `None` branch serves the disclosed surface.
Each capability keeps a thin adapter that names its artifact, keeps its
memoization, and preserves its public API. One genuine difference is now
declared rather than implied: `load_sealed_ledger(..., missing_ok=True)`
distinguishes a ledger a capability *ships with* (absence = broken deployment,
refuse) from one whose practice volume is still being built (absence = no class
has earned anything, serve disclosed). Curriculum serving is the second kind
today (ADR-0262 §5.1).
## 3. Why this is safe
The extraction's safety property is byte-identity: `seal_artifact` and
`write_sealed_ledger` reproduce exactly what the bespoke sealers wrote, so
re-sealing through the bridge leaves every committed artifact and every lane
pin unmoved. That is asserted, not assumed —
`test_committed_deduction_ledger_reseals_byte_identically` re-derives the
committed 25-band ledger through the bridge and compares the bytes.
## 4. What this unblocks
A new subject arena now needs a gold corpus and a band key. It does not need to
re-implement ratification — which is what the plan meant by "so each subject
arena plugs in without bespoke wiring", and why §3 sequenced this ahead of the
second subject.
## 5. Scope-outs
- **The estimation adapter keeps its predicate → converse-class naming.** That
mapping is genuinely local to estimation; the bridge gates a class name and
does not know how one is derived.
- **No ledger migration.** All three artifacts already share the schema shape;
nothing is rewritten, versioned, or moved.
- **`core/learning_arena`'s practice engine is untouched.** The bridge covers
the consumption half (seal → serve); the production half (run practice, tally)
is already shared.
## 6. Verification
`tests/test_ratified_ledger_bridge.py` — 8 tests: round-trip, hand-edit
rejection, required-vs-optional absence, absent class never licensed, the
Wilson volume floor, a single `wrong` costing the license, committed-ledger
byte-identity, and a guard that all three adapters read through the bridge
(they no longer import a hashing module of their own). Plus the existing
consumers unchanged: `core test --suite deductive` 252 passed; the estimation
and license test set 355 passed.

View file

@ -18,11 +18,11 @@ commit and SHA-verify on load.
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
from core.learning_arena.engine import run_practice
from core.ratified_ledger import seal_artifact, tally_dict, write_sealed_ledger
from core.reliability_gate import Action, Ceilings, ClassTally, license_for
from evals.deduction_serve.practice.gold import (
ConstructionGoldTether,
@ -30,7 +30,6 @@ from evals.deduction_serve.practice.gold import (
all_gold_problems,
assert_corpus_sound,
)
from formation.hashing import sha256_of
#: The committed sealed ledger lives next to its serving READER (chat/), mirroring
#: the estimation ledger's topology (producer in evals/, artifact by the reader).
@ -46,13 +45,7 @@ def build_ledger() -> dict[str, ClassTally]:
def _tally_dict(tally: ClassTally) -> dict[str, Any]:
return {
"correct": tally.correct,
"wrong": tally.wrong,
"refused": tally.refused,
"t2_verified": tally.t2_verified,
"t2_agrees_gold": tally.t2_agrees_gold,
}
return tally_dict(tally)
def run(ceilings: Ceilings | None = None) -> dict[str, Any]:
@ -81,31 +74,30 @@ def run(ceilings: Ceilings | None = None) -> dict[str, Any]:
def build_sealed_artifact() -> dict[str, Any]:
"""The committed sealed-ledger dict (self-verifying ``content_sha256``)."""
ledger = build_ledger()
classes = {cls: _tally_dict(tally) for cls, tally in sorted(ledger.items())}
return {
"schema": "deduction_serve_ledger_v1",
"classes": classes,
"content_sha256": sha256_of(classes),
"note": (
"""The committed sealed-ledger dict (self-verifying ``content_sha256``).
Formatting and hashing come from the shared bridge (ADR-0263) byte
-identical to what this module wrote before the extraction, which is how
the refactor is proven safe: re-sealing must not move the artifact.
"""
return seal_artifact(
build_ledger(),
schema="deduction_serve_ledger_v1",
note=(
"Sealed-practice committed ledger for deduction serving (ADR-0256). "
"Engine reads, never writes. Ceilings stay at safe defaults "
"(theta_SERVE=0.99). A band earns SERVE by demonstrated pipeline "
"reliability (reader+projector+engine) at volume >= 657 committed."
),
"provenance": "evals.deduction_serve.practice.runner.seal_ledger",
}
provenance="evals.deduction_serve.practice.runner.seal_ledger",
)
def seal_ledger(path: Path = _SEALED_LEDGER_PATH) -> dict[str, Any]:
"""Regenerate + write the committed sealed ledger. Verifies corpus soundness
against the independent oracle first (a mis-stated gold can never seal)."""
assert_corpus_sound()
artifact = build_sealed_artifact()
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(artifact, indent=2, sort_keys=True) + "\n", encoding="utf-8")
return artifact
return write_sealed_ledger(path, build_sealed_artifact())
def main(argv: list[str] | None = None) -> int:

View file

@ -8,27 +8,28 @@ never writes it. The artifact is the sealed-practice output of
load, so a hand-edited (un-ratified) ledger is rejected rather than silently trusted.
Determinism: the ledger is immutable ratified data, parsed once and cached; the gate
(``license_for``) is pure. No engine self-authorization ceilings stay at the safe
defaults (raising one's own bar is structurally impossible, ADR-0175 invariant #4).
is pure. No engine self-authorization ceilings stay at the safe defaults (raising
one's own bar is structurally impossible, ADR-0175 invariant #4).
The load/verify/gate mechanics live in ``core.ratified_ledger`` (ADR-0263), the
bridge extracted from this module and its two successors; this is the estimation
ADAPTER over it, preserving its own public API (including the predicate
converse-class naming, which is the one thing genuinely local to estimation).
"""
from __future__ import annotations
import json
from functools import lru_cache
from pathlib import Path
from core.reliability_gate import Action, Ceilings, ClassTally, LicenseDecision, license_for
from formation.hashing import sha256_of
from core.ratified_ledger import RatifiedLedgerError, load_sealed_ledger
from core.ratified_ledger import serve_license as _serve_license
from core.reliability_gate import Ceilings, ClassTally, LicenseDecision
from generate.determine.estimate import converse_class_name
_LEDGER_PATH = Path(__file__).resolve().parent / "data" / "estimation_ledger.json"
class RatifiedLedgerError(ValueError):
"""The committed estimation ledger is missing, malformed, or tampered with."""
@lru_cache(maxsize=1)
def load_ratified_ledger() -> dict[str, ClassTally]:
"""Load + verify the ratified estimation ledger → per-class ``ClassTally``.
@ -37,30 +38,7 @@ def load_ratified_ledger() -> dict[str, ClassTally]:
recomputed ``content_sha256`` does not match the committed one (tamper-evidence:
only the sealed-practice output is trusted, never a hand-edited ledger).
"""
try:
artifact = json.loads(_LEDGER_PATH.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc: # pragma: no cover - defensive
raise RatifiedLedgerError(f"cannot read ratified ledger: {exc}") from exc
classes = artifact.get("classes")
if not isinstance(classes, dict):
raise RatifiedLedgerError("ratified ledger has no 'classes' table")
if sha256_of(classes) != artifact.get("content_sha256"):
raise RatifiedLedgerError(
"ratified ledger content_sha256 mismatch — not the sealed-practice output"
)
ledger: dict[str, ClassTally] = {}
for cls, counts in classes.items():
ledger[cls] = ClassTally(
class_name=cls,
correct=int(counts.get("correct", 0)),
wrong=int(counts.get("wrong", 0)),
refused=int(counts.get("refused", 0)),
t2_verified=int(counts.get("t2_verified", 0)),
t2_agrees_gold=int(counts.get("t2_agrees_gold", 0)),
)
return ledger
return load_sealed_ledger(_LEDGER_PATH)
def serve_license(
@ -76,11 +54,7 @@ def serve_license(
deterministic ``license_for`` verdict under the safe default ceilings.
"""
ledger = ledger if ledger is not None else load_ratified_ledger()
tally = ledger.get(converse_class_name(predicate))
if tally is None:
return None
ceilings = ceilings if ceilings is not None else Ceilings.default()
return license_for(tally, Action.SERVE, ceilings)
return _serve_license(converse_class_name(predicate), ledger, ceilings=ceilings)
__all__ = ["RatifiedLedgerError", "load_ratified_ledger", "serve_license"]

View file

@ -0,0 +1,113 @@
"""The ratified-ledger bridge (ADR-0263) — seal → ratify → SHA-verify → gate.
These tests pin the four rules the three adapters depend on, and the property
that made the extraction safe: re-sealing through the bridge reproduces the
committed artifacts byte-for-byte.
"""
from __future__ import annotations
import json
import pytest
from core.ratified_ledger import (
RatifiedLedgerError,
load_sealed_ledger,
seal_artifact,
serve_license,
write_sealed_ledger,
)
from core.reliability_gate import ClassTally
def _tally(name: str, correct: int, wrong: int = 0) -> ClassTally:
return ClassTally(class_name=name, correct=correct, wrong=wrong, refused=0)
def test_seal_then_load_round_trips(tmp_path) -> None:
ledger = {"alpha": _tally("alpha", 700), "beta": _tally("beta", 12)}
path = tmp_path / "ledger.json"
write_sealed_ledger(
path, seal_artifact(ledger, schema="t_v1", note="n", provenance="p")
)
loaded = load_sealed_ledger(path)
assert set(loaded) == {"alpha", "beta"}
assert loaded["alpha"].correct == 700
def test_a_hand_edited_ledger_is_rejected(tmp_path) -> None:
"""Rule 2 — tamper-evidence is structural. Editing a tally without
re-sealing does not produce a slightly-wrong ledger; it produces an
unratified one, and loading REFUSES."""
path = tmp_path / "ledger.json"
write_sealed_ledger(
path,
seal_artifact({"alpha": _tally("alpha", 10)}, schema="t_v1", note="n", provenance="p"),
)
artifact = json.loads(path.read_text())
artifact["classes"]["alpha"]["correct"] = 9_999
path.write_text(json.dumps(artifact, indent=2, sort_keys=True) + "\n")
with pytest.raises(RatifiedLedgerError):
load_sealed_ledger(path)
def test_missing_ledger_refuses_unless_declared_optional(tmp_path) -> None:
"""A capability that SHIPS with a ledger is broken without it; one whose
practice volume is still being built legitimately has none. Neither is
answered by guessing a license."""
path = tmp_path / "absent.json"
with pytest.raises(RatifiedLedgerError):
load_sealed_ledger(path)
assert load_sealed_ledger(path, missing_ok=True) == {}
def test_absent_class_is_never_licensed() -> None:
"""Rule 4 — no evidence is not a license; the caller's ``None`` branch is
what serves the disclosed surface."""
assert serve_license("nobody", {"alpha": _tally("alpha", 700)}) is None
def test_volume_floor_is_enforced_by_the_gate() -> None:
"""A perfect but small record does not clear θ_SERVE=0.99 — the Wilson
floor is what makes a license *earned* rather than merely clean."""
small = serve_license("alpha", {"alpha": _tally("alpha", 12)})
large = serve_license("alpha", {"alpha": _tally("alpha", 720)})
assert small is not None and not small.licensed
assert large is not None and large.licensed
def test_a_single_wrong_costs_the_license() -> None:
dirty = serve_license("alpha", {"alpha": _tally("alpha", 720, wrong=1)})
assert dirty is not None and not dirty.licensed
def test_committed_deduction_ledger_reseals_byte_identically() -> None:
"""The extraction's safety property: the bridge writes what the bespoke
sealers wrote, so adopting it moves no committed artifact and no lane pin."""
from pathlib import Path
from evals.deduction_serve.practice.runner import build_sealed_artifact
committed = Path("chat/data/deduction_serve_ledger.json")
expected = json.dumps(build_sealed_artifact(), indent=2, sort_keys=True) + "\n"
assert committed.read_text(encoding="utf-8") == expected
def test_every_adapter_reads_through_the_bridge() -> None:
"""All three instances now share one loader — the property that makes a
future change to the ratification rule land everywhere at once."""
import chat.curriculum_serve_license as curriculum
import chat.deduction_serve_license as deduction
import generate.determine.estimation_license as estimation
for module in (deduction, estimation, curriculum):
source = module.__file__ or ""
assert source
text = open(source, encoding="utf-8").read()
assert "core.ratified_ledger" in text
# The signal that an adapter still verifies for itself is that it
# hashes for itself; docstrings may (and do) still explain the rule.
assert "formation.hashing" not in text, (
f"{module.__name__} still re-implements verification"
)