Audit of the one-mutation-path invariant (ADR-0021 §3) found three leaks
where pack authority or session-state writes could substitute for coherence
judgment. All three landed fixes or partial closures in this push.
Leaks closed:
- Leak A: pack vocab defaulted to COHERENT — flipped to SPECULATIVE in
language_packs/{compiler,schema}.py; docstring corrected to align with
ADR-0021 (it was rationalizing the leak).
- Leak B: vault.recall was epistemic-blind — VaultStore.store() now stamps
every entry with EpistemicStatus (default SPECULATIVE); recall(min_status=)
filters to admissible-as-evidence tier. All 4 vault-write sites updated.
- Leak C (write-side): generate/proposition.py:198 stored articulated
propositions unmarked — now stamps SPECULATIVE, breaking the
fabrication-feedback loop in principle. Read-side audit of 5 call sites
is the residual.
New architectural invariants (tests/test_architectural_invariants.py):
- INV-21: one-mutation-path allowlist (caught Leak C on first run)
- INV-22: pack lexicon default is SPECULATIVE (Leak A guard)
- INV-23: vault recall epistemic-aware (Leak B guard)
New eval lanes:
- teaching_injection_resistance — ships GREEN at 1.00/1.00/0 (the
structural anti-injection claim is real and measurable)
- refusal_calibration — honest gap: 0% refusal, 0% fabrication
- contradiction_detection — honest gap: 50% flag via versor-delta heuristic,
100% false-positive; motivates the proper coherence-checker
- articulation_of_status — honest gap: 0% speculative articulation, 60%
false certainty; output-side leak surface
New benchmarks:
- benchmarks/footprint.py — total deployed runtime is 7.06 MiB
(109,358x smaller than Llama 3.1 405B, runs offline, no GPU)
- benchmarks/learning_curve.py — monotonic + replay-deterministic curve
per lane
Documentation:
- docs/truth_seeking_schema.md — foundational architectural commitment,
five rules, mapped to human failure modes, leaks published openly
- evals/CLAIMS.md — five-tier public claims doc; Tier 4.5 publishes
known gaps with named fixes; verification contract at top
- README.md — new pillar between algebraic substrate and language pillar
Includes in-flight formation pipeline scaffolding (formation/, tests/formation/,
docs/formation_pipeline_plan.md) and minor CLI/contracts/gitignore edits
that were already in the working tree at session start.
Verification: 798 passed, 2 skipped, 1 deselected (pre-existing pack-count
test drift unrelated to schema changes).
298 lines
11 KiB
Python
298 lines
11 KiB
Python
"""Learning-curve bench — capability vs reviewed-teaching depth.
|
|
|
|
Runs an eval lane at successive teaching depths (e.g. 0, 5, 10, 25,
|
|
50, 100 reviewed corrections) sharing one TeachingStore across the
|
|
pipelines instantiated for each case. Produces:
|
|
|
|
- A monotonic score curve per lane.
|
|
- A per-step trace-hash digest proving every intermediate post-teaching
|
|
state is byte-identical across reruns (the deterministic-replay claim
|
|
applied to the teaching loop).
|
|
|
|
The bench is the load-bearing demo for Tier 3 of evals/CLAIMS.md:
|
|
"N corrections -> +X% on lane L, locked deterministically, replayable
|
|
forever." Without it, the teaching-loop claim is unfalsifiable from
|
|
the outside.
|
|
|
|
Usage:
|
|
|
|
from benchmarks.learning_curve import run_learning_curve
|
|
curve = run_learning_curve(
|
|
lane="cognition",
|
|
cycles=(0, 5, 10, 25, 50, 100),
|
|
teaching_examples=load_my_corrections(),
|
|
)
|
|
assert curve.is_monotonic
|
|
assert curve.replay_deterministic
|
|
print(curve.summary())
|
|
|
|
CLI surface (added to core/cli.py separately):
|
|
|
|
core bench learning-curve <lane> --cycles 0,5,25,100
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any, Iterable
|
|
|
|
from chat.runtime import ChatRuntime
|
|
from core.cognition.pipeline import CognitiveTurnPipeline
|
|
from evals.framework import get_lane, load_cases
|
|
from generate.intent import DialogueIntent, IntentTag
|
|
from teaching.correction import CorrectionCandidate, _candidate_id
|
|
from teaching.epistemic import EpistemicStatus
|
|
from teaching.review import ReviewOutcome, ReviewedTeachingExample, _review_hash
|
|
from teaching.store import TeachingStore
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class TeachingExampleSpec:
|
|
"""Minimal spec to synthesize a ReviewedTeachingExample for replay.
|
|
|
|
Bench corrections are reviewed examples that are content-addressed
|
|
by (subject, correction_text). They must not reference live dialogue
|
|
state, otherwise the curve depends on session order and stops being
|
|
replayable.
|
|
"""
|
|
subject: str
|
|
correction_text: str
|
|
|
|
def to_reviewed(self) -> ReviewedTeachingExample:
|
|
intent = DialogueIntent(tag=IntentTag.CORRECTION, subject=self.subject)
|
|
prior_surface = ""
|
|
prior_turn = 0
|
|
cand = CorrectionCandidate(
|
|
correction_text=self.correction_text,
|
|
intent=intent,
|
|
prior_surface=prior_surface,
|
|
prior_turn=prior_turn,
|
|
candidate_id=_candidate_id(self.correction_text, prior_surface, prior_turn),
|
|
)
|
|
outcome = ReviewOutcome.ACCEPTED
|
|
return ReviewedTeachingExample(
|
|
candidate=cand,
|
|
outcome=outcome,
|
|
review_hash=_review_hash(cand, outcome),
|
|
epistemic_status=EpistemicStatus.SPECULATIVE,
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class CurvePoint:
|
|
cycle: int
|
|
metrics: dict[str, float]
|
|
store_digest: str
|
|
lane_trace_digest: str
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class LearningCurveReport:
|
|
lane: str
|
|
version: str
|
|
primary_metric: str
|
|
points: tuple[CurvePoint, ...]
|
|
replay_points: tuple[CurvePoint, ...]
|
|
|
|
@property
|
|
def is_monotonic(self) -> bool:
|
|
scores = [p.metrics.get(self.primary_metric, 0.0) for p in self.points]
|
|
return all(b >= a for a, b in zip(scores, scores[1:]))
|
|
|
|
@property
|
|
def replay_deterministic(self) -> bool:
|
|
if len(self.points) != len(self.replay_points):
|
|
return False
|
|
for first, second in zip(self.points, self.replay_points):
|
|
if first.store_digest != second.store_digest:
|
|
return False
|
|
if first.lane_trace_digest != second.lane_trace_digest:
|
|
return False
|
|
return True
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"lane": self.lane,
|
|
"version": self.version,
|
|
"primary_metric": self.primary_metric,
|
|
"is_monotonic": self.is_monotonic,
|
|
"replay_deterministic": self.replay_deterministic,
|
|
"points": [
|
|
{
|
|
"cycle": p.cycle,
|
|
"metrics": p.metrics,
|
|
"store_digest": p.store_digest,
|
|
"lane_trace_digest": p.lane_trace_digest,
|
|
}
|
|
for p in self.points
|
|
],
|
|
}
|
|
|
|
def summary(self) -> str:
|
|
head = f"learning_curve[{self.lane}@{self.version}] metric={self.primary_metric}"
|
|
rows = "\n".join(
|
|
f" cycle={p.cycle:>4} {self.primary_metric}={p.metrics.get(self.primary_metric, 0.0):.4f} "
|
|
f"trace={p.lane_trace_digest[:12]} store={p.store_digest[:12]}"
|
|
for p in self.points
|
|
)
|
|
flags = (
|
|
f" monotonic={self.is_monotonic} replay_deterministic={self.replay_deterministic}"
|
|
)
|
|
return f"{head}\n{rows}\n{flags}"
|
|
|
|
|
|
def _store_digest(store: TeachingStore) -> str:
|
|
payload = json.dumps(
|
|
[p.as_dict() for p in store.pending_proposals()],
|
|
sort_keys=True,
|
|
ensure_ascii=False,
|
|
)
|
|
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def _lane_trace_digest(case_details: list[dict[str, Any]]) -> str:
|
|
hashes = [d.get("trace_hash", "") for d in case_details]
|
|
return hashlib.sha256("|".join(hashes).encode("utf-8")).hexdigest()
|
|
|
|
|
|
def _build_store(specs: Iterable[TeachingExampleSpec], depth: int) -> TeachingStore:
|
|
"""Construct a fresh store and add the first `depth` specs in order.
|
|
|
|
Re-built per depth (rather than mutated in place) so each curve
|
|
point is reproducible from `(specs, depth)` alone — not from the
|
|
history of a long-lived process.
|
|
"""
|
|
store = TeachingStore(capacity=max(depth, 1) * 4 + 8)
|
|
for spec in list(specs)[:depth]:
|
|
store.add(spec.to_reviewed())
|
|
return store
|
|
|
|
|
|
def _run_lane_with_store(
|
|
lane_cases: list[dict[str, Any]],
|
|
store: TeachingStore,
|
|
) -> tuple[dict[str, float], list[dict[str, Any]]]:
|
|
"""Run cognition-lane-style cases through pipelines that share `store`.
|
|
|
|
We do not go through `evals.framework.run_lane` because the framework
|
|
runner constructs its own pipeline per case with a fresh empty store.
|
|
To make the teaching depth load-bearing, the bench must inject the
|
|
store. The metric extraction below intentionally mirrors
|
|
`evals/cognition/runner.py` so the curve numbers are directly
|
|
comparable to the published lane scores.
|
|
"""
|
|
from evals.cognition.runner import _run_case
|
|
|
|
total = 0
|
|
intent_correct = 0
|
|
terms_expected = 0
|
|
terms_captured = 0
|
|
surface_grounded = 0
|
|
versor_closures = 0
|
|
case_details: list[dict[str, Any]] = []
|
|
|
|
for case in lane_cases:
|
|
runtime = ChatRuntime()
|
|
pipeline = CognitiveTurnPipeline(runtime, teaching_store=store)
|
|
cr = _run_case(case, pipeline)
|
|
|
|
total += 1
|
|
if cr.intent_correct:
|
|
intent_correct += 1
|
|
terms_expected += len(cr.terms_expected)
|
|
terms_captured += len(cr.terms_captured)
|
|
if cr.surface_contains_pass:
|
|
surface_grounded += 1
|
|
if cr.versor_closure:
|
|
versor_closures += 1
|
|
|
|
case_details.append({
|
|
"case_id": cr.case_id,
|
|
"intent_correct": cr.intent_correct,
|
|
"surface_contains_pass": cr.surface_contains_pass,
|
|
"versor_closure": cr.versor_closure,
|
|
"versor_condition": round(cr.versor_condition, 9),
|
|
"trace_hash": cr.trace_hash,
|
|
})
|
|
|
|
metrics = {
|
|
"total": float(total),
|
|
"intent_accuracy": round(intent_correct / total, 4) if total else 0.0,
|
|
"term_capture_rate": round(terms_captured / terms_expected, 4) if terms_expected else 1.0,
|
|
"surface_groundedness": round(surface_grounded / total, 4) if total else 0.0,
|
|
"versor_closure_rate": round(versor_closures / total, 4) if total else 0.0,
|
|
}
|
|
return metrics, case_details
|
|
|
|
|
|
def run_learning_curve(
|
|
*,
|
|
lane: str,
|
|
cycles: tuple[int, ...],
|
|
teaching_examples: Iterable[TeachingExampleSpec],
|
|
version: str = "v1",
|
|
split: str = "public",
|
|
primary_metric: str = "intent_accuracy",
|
|
verify_replay: bool = True,
|
|
) -> LearningCurveReport:
|
|
"""Run `lane` at each teaching depth in `cycles`, return a curve report.
|
|
|
|
`verify_replay` re-runs the entire curve a second time and asserts
|
|
both the per-step store digest and the per-step lane trace digest
|
|
are byte-identical to the first pass. This is the deterministic
|
|
replay claim applied to the teaching loop, not just to one prompt.
|
|
"""
|
|
lane_info = get_lane(lane)
|
|
cases_path = lane_info.public_cases_path(version) if split == "public" else lane_info.dev_cases_path()
|
|
cases = load_cases(cases_path)
|
|
specs = tuple(teaching_examples)
|
|
|
|
def _one_pass() -> tuple[CurvePoint, ...]:
|
|
out: list[CurvePoint] = []
|
|
for depth in cycles:
|
|
store = _build_store(specs, depth)
|
|
metrics, details = _run_lane_with_store(cases, store)
|
|
out.append(CurvePoint(
|
|
cycle=depth,
|
|
metrics=metrics,
|
|
store_digest=_store_digest(store),
|
|
lane_trace_digest=_lane_trace_digest(details),
|
|
))
|
|
return tuple(out)
|
|
|
|
first = _one_pass()
|
|
second = _one_pass() if verify_replay else first
|
|
|
|
return LearningCurveReport(
|
|
lane=lane,
|
|
version=version,
|
|
primary_metric=primary_metric,
|
|
points=first,
|
|
replay_points=second,
|
|
)
|
|
|
|
|
|
def write_curve(report: LearningCurveReport, root: Path | None = None) -> Path:
|
|
"""Persist a curve report to evals/reports/learning_curves/<lane>.json."""
|
|
base = root or Path(__file__).resolve().parent.parent / "evals" / "reports" / "learning_curves"
|
|
base.mkdir(parents=True, exist_ok=True)
|
|
path = base / f"{report.lane}.json"
|
|
path.write_text(
|
|
json.dumps(report.as_dict(), ensure_ascii=False, indent=2, sort_keys=True) + "\n"
|
|
)
|
|
return path
|
|
|
|
|
|
DEFAULT_COGNITION_TEACHING: tuple[TeachingExampleSpec, ...] = (
|
|
TeachingExampleSpec(subject="truth", correction_text="truth is coherence"),
|
|
TeachingExampleSpec(subject="knowledge", correction_text="knowledge is justified true belief"),
|
|
TeachingExampleSpec(subject="wisdom", correction_text="wisdom is applied knowledge"),
|
|
TeachingExampleSpec(subject="light", correction_text="light is electromagnetic radiation"),
|
|
TeachingExampleSpec(subject="meaning", correction_text="meaning is reference plus use"),
|
|
TeachingExampleSpec(subject="concept", correction_text="a concept is a coherent abstraction"),
|
|
TeachingExampleSpec(subject="coherence", correction_text="coherence is mutual support among propositions"),
|
|
TeachingExampleSpec(subject="proposition", correction_text="a proposition is a truth-apt claim"),
|
|
)
|