Squashes the arc's work into one commit; the workflow-file edit it originally carried is excluded (see the end of this message). ## Lane 1 — Workbench recorded a proved answer as ungrounded With deduction_serving_enabled ratified ON (ADR-0256), workbench/api.py's live chat route builds a bare ChatRuntime(), so the deduction composer decides Workbench turns and stamps grounding_source="deduction" — but _coerce_grounding_source carried a hand-copied whitelist of the six pre-arc labels and silently rewrote anything else to "none". The runtime comment reasoned this was inert because "REPL turns do not flow through Workbench's CognitivePipelineRecord path". True, and irrelevant: the traffic flows the other way. Stale since 2026-07-24. Scope is one field. workbench/api.py:818 prefers TurnEvent.epistemic_state, which read epistemic_state_needed — honest. So the UNregistered path degraded honestly while the hand-copied whitelist asserted a falsehood; a second copy of a closed enum was worse than no copy. Hence registration AND derivation: GROUNDING_SOURCES exposes the Literal's members, and the coercion reads it. workbench-ui badges/tokens/snapshot follow; enumCoverage.test.ts forces atomicity. ## Lane 2 — the ratification ceremony The discovery loop was instrumented but not closed. teaching/ratification.py turns a reviewed decision into a chain record, a corpus commit, and a receipt. Its design turns on one observation: _ratified_rows DROPS unadmissible rows silently — correct when serving, a trap when ratifying, because the file grows, the commit lands, and the band count does not move. So the ceremony refuses to call an append a ratification until it has re-read the curriculum through the real loader and seen the chain arrive; a non-admitted append is rolled back. Validation is a pre-flight courtesy, admission is the proof. Arena queue entry and ledger reseal are deliberately NOT performed (bridge rule 1); the receipt names them. Front door: `core proposal-queue ratify`, a sibling of `review` rather than a flag on it. ## Lane 3 — structural closures - ADR-0263 gains rule 5: absence policy is DECLARED in CAPABILITY_LEDGERS, not passed at the call site. An AST-matched test fails if a serving path passes missing_ok again. - Deductive suite added WHOLE to the pre-push gate: 285 tests in 29s against smoke's 216 in 62s, so no coverage trade was needed. - Smoke/CI parity assertion made bidirectional. It was one-directional, and had drifted. - test_prior_surface_deduction_binding.py pins correction binding on the deduction path. The review's diagnosis did NOT reproduce — hash_surface moves in lockstep — so it pins what is there. Mutation-checked. - Domain-keyed ADR index over 312 flat-numbered files, explicitly partial. - Arc-close brief template, plus this arc's own brief filled in against it. ## Lanes 4 and 5 — two premises falsified by measurement, one of them mine Math 4.2: baseline reproduced (correct=5 wrong=0 refused=495); all four named cases traced to one seam with each gap isolated by one-variable probes. Then the number that changes the recommendation: the gap blocking case 0000 affects 1 case in 500, the 'than' gap blocking 0001 affects 2. ADR-0251's prohibition on per-case growth now rests on a count. No reader change made. CGA: versor_condition is 0.22% of a turn, not the "~10x proof latency" I claimed — that multiplied an isolated microbenchmark by a call count and compared it to a single verdict's latency. The real cost is geometric_product at 33,986 calls/turn (~73%) via cga_inner in search paths. The obvious closed form is NOT bit-exact (954/4000 in f32); backend.vault_recall's serial fold IS (3000/3000, worst-rel 0) and is the correct target. cargo test could not run — static.crates.io is denied by the sandbox network policy — so the Rust parity question stays open and the typestate lane is carried forward, not shipped uncompiled. ## Not landed: three lines owed to .github/workflows/smoke.yml The CI smoke gate is narrower than the local one — test_pack_draft_serve_boundary.py (ADR-0253 INV-33) has been local-only, unseen because the parity pin checked one direction. The edit was authored and rejected at push for lacking the `workflow` OAuth scope, so it is recorded as a named, dated PENDING_IN_CI exception rather than dropped: the assertion still fires on any new divergence, and a second guard fires once the three land. [Verification]: pre-push gates all green — smoke 236 passed, warmed_session 10 passed, deductive 285 passed. Ratification 14, ADR index 5, CLI suites 10. Grounding/epistemic sweep 741 passed 1 skipped. workbench-ui 598 passed across 73 files, tsc -b clean. capability index 11 passed, digest unchanged. Math holdout correct=5 wrong=0 refused=495. Committed chain corpora byte-unchanged after the tests that write to them. Environment caveat: the repo pins requires-python ==3.12.13, which uv cannot fetch for linux-x86_64, so `uv sync --locked` fails. All Python runs used a scratch venv on 3.12.11 with declared deps — not the locked universe, not the full ~12k suite. The pin was left untouched. Re-run on a 3.12.13 host before treating this as merge evidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
193 lines
8.5 KiB
Python
193 lines
8.5 KiB
Python
"""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 ast
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from core.ratified_ledger import (
|
|
CAPABILITY_LEDGERS,
|
|
RatifiedLedgerError,
|
|
load_capability_ledger,
|
|
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"
|
|
)
|
|
|
|
|
|
class TestCapabilityManifest:
|
|
"""Rule 5 — absence policy is DECLARED, not passed at the call site.
|
|
|
|
``missing_ok`` answers a question about the capability ("does this ship
|
|
with a ledger, or is its practice volume still being built?"), not about
|
|
the call. While it was only a ``load_sealed_ledger`` keyword, any adapter
|
|
onboarding a new subject through the bridge could pass ``True`` and quietly
|
|
turn a should-be-hard-refuse into a disclosed hedge, with nothing to catch
|
|
it. These pin the manifest that took the choice away from the call site.
|
|
"""
|
|
|
|
def test_every_registered_ledger_resolves_inside_the_repo(self) -> None:
|
|
"""Same class as the ``derived_close_proposals`` ``parents[3]`` bug: a
|
|
path constant that silently pointed outside the tree for five weeks
|
|
because the feature was default-off and nothing asserted the path."""
|
|
root = Path(__file__).resolve().parents[1]
|
|
for spec in CAPABILITY_LEDGERS.values():
|
|
assert root in spec.path.parents, (
|
|
f"{spec.capability}: ledger path escapes the repo: {spec.path}"
|
|
)
|
|
|
|
def test_shipping_capabilities_are_declared_required(self) -> None:
|
|
"""A ledger that exists on disk must not be declared optional — that
|
|
combination reads "absence is fine" about a capability whose absence
|
|
would in fact be a broken deployment."""
|
|
for spec in CAPABILITY_LEDGERS.values():
|
|
if spec.path.exists():
|
|
assert spec.missing_ok is False, (
|
|
f"{spec.capability} ships a committed ledger but is "
|
|
"registered missing_ok=True"
|
|
)
|
|
|
|
def test_deduction_serve_is_required_and_loads(self) -> None:
|
|
ledger = load_capability_ledger("deduction_serve")
|
|
assert len(ledger) == 25, "the 25 sealed shape-bands (ADR-0256)"
|
|
assert all(tally.wrong == 0 for tally in ledger.values())
|
|
|
|
def test_curriculum_serve_is_optional_and_empty_today(self) -> None:
|
|
"""ADR-0262 §5 — no band has earned a license from present volume."""
|
|
assert CAPABILITY_LEDGERS["curriculum_serve"].missing_ok is True
|
|
assert load_capability_ledger("curriculum_serve") == {}
|
|
|
|
def test_unregistered_capability_refuses(self) -> None:
|
|
"""A capability the manifest never declared has no absence policy to
|
|
inherit, so it cannot be consumed at all."""
|
|
with pytest.raises(RatifiedLedgerError, match="unregistered"):
|
|
load_capability_ledger("philosophy_serve")
|
|
|
|
def test_no_production_adapter_passes_missing_ok(self) -> None:
|
|
"""The keyword survives on the primitive for tests and one-off tooling.
|
|
If a serving path starts passing it again, the manifest has been routed
|
|
around and this fails.
|
|
|
|
Matched on the AST rather than the text: the string ``missing_ok``
|
|
appears legitimately in the adapters' docstrings, which explain the
|
|
policy they inherit. What must not appear is an *argument*.
|
|
"""
|
|
root = Path(__file__).resolve().parents[1]
|
|
offenders: list[str] = []
|
|
for directory in ("chat", "core", "generate", "teaching", "workbench"):
|
|
for path in sorted((root / directory).rglob("*.py")):
|
|
if path.name == "ratified_ledger.py":
|
|
continue # the primitive's own definition site
|
|
tree = ast.parse(path.read_text(encoding="utf-8"))
|
|
for node in ast.walk(tree):
|
|
if not isinstance(node, ast.Call):
|
|
continue
|
|
func = node.func
|
|
name = func.attr if isinstance(func, ast.Attribute) else getattr(func, "id", "")
|
|
if name != "load_sealed_ledger":
|
|
continue
|
|
if any(kw.arg == "missing_ok" for kw in node.keywords):
|
|
offenders.append(f"{path.relative_to(root)}:{node.lineno}")
|
|
assert offenders == [], f"missing_ok passed outside the manifest: {offenders}"
|