arborist/tests/test_fivef_threshold_calibration.py
russell@unturf.com d78dccc8ed
#000025 §10.11 + §10.13 + §10.14 — close the 5F battery
Closes the three open Phase-1b items of #000025; every §10 closure
criterion is now met, so the ticket flips to closed.

§10.14 — ForkScore threshold-calibration handoff to #000012.
bench/scripts/fivef_threshold_calibration.py (make bench-5f-threshold-
calibration) runs the canonical 5S/5T/5F packs + the 5F live packs and
reports baseline rates, observability granularity (1/n), and fork_score
verdicts on the parent vs synthetic child perturbations →
bench/results/5f-threshold-calibration-2026-05-11.md. Findings written
into ticket-000012 §8: keep SIGNAL_FLOOR / HARD_REGRESSION_FLOOR at
0.05; the small 5S packs (syntax n=10, semantics n=8) are coarser than
the floors so any regression there trips hard-reject (intended zero-
tolerance); the 5x averaging dilution in _delta_*; ceiling saturation
(every pack at 1.0 -> delta-rate terms <= 0). No constant change
shipped. 6 tests in tests/test_fivef_threshold_calibration.py.

§10.13 — feedback latency / efficiency on real workload.
run_feedback_loop now computes feedback_latency (listed in §5.5 since
Phase 1a, never implemented) — wall-clock seconds to apply a live
chain against its temp shard, surfaced per-task
(feedback_latency_seconds) + battery (feedback_latency_mean_seconds,
feedback_live_task_count). For live chains feedback_efficiency's cost
denominator switched from len(chain) (count of requested ops) to the
persisted footprint _persisted_cost = audit-event rows the chain
actually wrote + their body bytes / 1e6. Embedded chains keep
len(chain) and report feedback_latency_seconds = None. Latency is a
wall-clock field (run-to-run variable, like BatteryResult.timestamp)
and is not a fork_score input. 3 tests in tests/test_bench_batteries.py.

§10.11 — real selfmodel finetuning chains.
bench/scripts/selfmodel_chain_snapshot.py (make bench-5f-selfmodel-
snapshot) appends one chained SelfModel snapshot per run to a
persistent shard (~/.arborist/shards/selfmodel-chain.db, override via
ARBORIST_SELFMODEL_CHAIN_DB) with one CapabilityClaim per sub-battery
(metric = "5S-syntax" etc., measured_value = that pack's rate,
eval_digest = the pack's fixture digest, threshold = SIGNAL_FLOOR).
snapshot() auto-parents, so each snapshot is a distinct root and the
lineage grows by one per run. run_finetuning gains a third dispatch
mode — shard-chain (gated on a task's selfmodel_shard key) — via
_chain_finetuning_measure: reads the two most-recent snapshots
(latest() = child, its parent_selfmodel_root = parent) and measures
improvement on target_capability between them. This is the real
lineage replacing Phase-1a's synthetic parent->child pairs; the
chained delta reflects genuine cross-run drift (0.0 today — the
embedded packs are at ceiling). Operator pack
bench/fixtures/5f/finetuning-shardchain-v1.jsonl (6 tasks) + make
bench-5f-finetuning-shardchain; not in `make bench-5f`, `make test`,
or a fresh checkout (a missing/too-short chain fails honestly). The
real chain shard was bootstrapped 2-deep on 2026-05-11; make
chain-check-shards reports 0 breaks on it (and all other shards).
10 tests in tests/test_selfmodel_chain.py.

Full suite: 2311 passed, 28 skipped.
2026-05-11 07:41:37 -04:00

86 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Tests for the #000025 §10.14 ForkScore threshold-calibration handoff
script (``bench/scripts/fivef_threshold_calibration.py``).
The script is pure measurement over the canonical 5S/5T/5F packs +
`fork_score`, so its markdown report is deterministic given fixed
fixtures + code. These tests pin the structural anchors and the
floor-constant sanity verdicts so the handoff doesn't drift silently.
The live 5F packs (``*-live-v1.jsonl``) spin up temp shards and add
~15s per run; the calibration *logic* (granularity, fork-score sanity)
doesn't depend on them, so the tests stub ``_LIVE_PACKS`` to empty.
The live runners are exercised by ``tests/test_bench_batteries.py``
and ``make bench-5f-live``.
"""
from __future__ import annotations
import pytest
from bench.scripts import fivef_threshold_calibration as calib
@pytest.fixture(scope="module", autouse=True)
def _no_live_packs():
saved = calib._LIVE_PACKS
calib._LIVE_PACKS = {}
try:
yield
finally:
calib._LIVE_PACKS = saved
@pytest.fixture(scope="module")
def report() -> str:
return calib.build_report()
def test_report_is_deterministic():
a = calib.build_report()
b = calib.build_report()
def _strip_ts(s: str) -> str:
return "\n".join(ln for ln in s.splitlines() if not ln.startswith("**Date:**"))
assert _strip_ts(a) == _strip_ts(b)
def test_report_has_structural_anchors(report: str):
assert report.startswith("# 5S/5T/5F → v8 ForkScore threshold-calibration handoff")
assert "#000025 §10.14" in report
assert "## 1. Baseline rates + granularity" in report
assert "## 2. Identity-fork verdict (no change)" in report
assert "## 3. Floor-constant sanity checks" in report
assert "## 4. Recommendation for #000012" in report
for sub in (
"syntax", "semantics", "syllogism", "synthesis", "semiotics",
"transfer-learning", "triangulation", "truthtables", "transitivity", "time",
"function", "finetuning", "falsification", "formulate", "feedback-loop",
):
assert f"| {sub} |" in report
def test_identity_fork_is_marginal(report: str):
# Packs are at ceiling at HEAD → every Δ-rate term is 0 → MARGINAL.
assert "`fork_score(parent, parent)` → **MARGINAL (score +0.0000)**" in report
def test_floor_sanity_verdicts_present(report: str):
assert "child recovers **all 5** 5F subs by +0.06 | ACCEPT" in report
assert "child recovers **only 1** 5F sub by +0.06 | MARGINAL" in report
assert "child drops one 5F sub by 0.05 | REJECT" in report
assert "REGRESSION_5F:5f/falsification" in report
def test_recommendation_keeps_floors_and_flags_dilution(report: str):
assert "Keep `SIGNAL_FLOOR = 0.05` and `HARD_REGRESSION_FLOOR = 0.05`" in report
assert "5× averaging dilution" in report
assert "Ceiling saturation" in report
assert "No constant change shipped" in report
def test_cli_main_writes_report(tmp_path):
out = tmp_path / "calib.md"
rc = calib.main(["--out", str(out)])
assert rc == 0
text = out.read_text(encoding="utf-8")
assert text.startswith("# 5S/5T/5F → v8 ForkScore threshold-calibration handoff")