Three small streams in one commit; each closes / expands a
recently-landed ticket without changing its hard contract.
#000026 Phase 3 wiring — authorship warrant ladder visible
============================================================
Phase 3 sidecar (arborist/qa/warrant_authorship.py landed in 60b5748)
exposed the classifier but didn't surface it. Two wirings:
- arborist/qa/inspect.py — diagnose_authorship_warrant runs against
the cached row's question + answer + per-source raw chunks +
URIs + titles; result lands as `authorship` field alongside the
other sidecars.
- arborist/cli.py _render_warrant_tail — appends ` · warrant:
<readable-tier>` when result['authorship'] is populated with a
non-quiet tier. AUTHOR_COPYRIGHT_FOOTER → "copyright-footer", etc.
NO_AUTHORSHIP_SIGNAL stays silent. Backward-compat: results
without an `authorship` key render unchanged.
Tests: 3 inspect-path tests (no-signal, copyright-footer,
repository-owner) + 4 render-tail tests (presence, no-signal
silence, missing-key silence, all-six-tiers readable mapping).
#000028 follow-ups — capital ledger + sample-rate
==================================================
Two policy fields layered on top of canonical_witness_enabled:
- canonical_witness_sample_rate (0.0..1.0; default 1.0). Operators
wanting passive calibration set 0.05 to fire witness on 5% of
canonical questions while paying 5% of LLM cost. 0.0 effectively
off; 1.0 = current always-on behavior. Gating uses random.random()
so distribution is uniform; clamped to [0, 1].
- Capital ledger row written for each FIRED witness (not skipped
ones). op_type='canonical_witness'; estimator inputs include
prompt_chars + answer_chars + llm_seconds + agreement_label +
pi_star_ref. Best-effort: ledger-write failure must never fail
the query (sidecar discipline).
Tests: 4 new — sample_rate=0.0 skips (no LLM call, no ledger row);
sample_rate=1.0 always fires; capital_ledger row written under
op_type='canonical_witness' with full input blob; sampled-out
witness records zero ledger rows.
Both fields fold into governance_policy_hash naturally via the
existing policy-hash machinery — flipping witness mode invalidates
prior records as expected.
#000025 Phase 1d — 5F fixture catalog 30 → 50
==============================================
Both synthetic and live sides of all 5 sub-batteries expanded
30 → 50 (+200 fixtures total: 5 × 20 synthetic, 5 × 20 live).
function — claim_count cycles 2..7 across new fixtures
falsification — 10-violation palette across new ids
feedback-loop — fact-N learning chains
finetuning — capability transitions across canonical π*
(math/logic/algebra/calculus pool)
formulate — multi-pointer claim shapes
500/500 pass through respective runners. test_session_integration
total bumped 562 → 662. Pinned test_5f_*_runs counts updated 30 →
50 (synthetic main + embedded + live).
Tests
=====
Full suite: 1467 passed, 36 skipped (was 1388; +79 across warrant
render + witness sample/ledger + 5F implicit coverage).
487 lines
16 KiB
Python
487 lines
16 KiB
Python
"""Tests for the multi-modality witness (Ticket #000028).
|
|
|
|
Three modalities — kernel, cache, alien (LLM) — fan out in parallel
|
|
over a canonical-shape question. Every modality's answer is
|
|
canonicalized through the same kernel for byte-comparison. The
|
|
witness records cross-modality agreement as a label.
|
|
|
|
These tests exercise ``arborist.qa.witness`` directly with
|
|
:class:`StubClient`. Live-LLM coverage lives under the bench harness;
|
|
this file stays offline.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from arborist.qa.client import StubClient
|
|
from arborist.qa.witness import (
|
|
Witness,
|
|
_canonicalize_via_kernel,
|
|
_classify_agreement,
|
|
run_witness,
|
|
)
|
|
|
|
|
|
# ----- _canonicalize_via_kernel ------------------------------------------
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"text,ref,expected",
|
|
[
|
|
("3/10", "arithmetic@v1", b"3/10"),
|
|
("0.3", "arithmetic@v1", b"3/10"),
|
|
("0.1 + 0.2", "arithmetic@v1", b"3/10"),
|
|
(" 3/10 ", "arithmetic@v1", b"3/10"),
|
|
("TRUE", "logic-kernel@v1", b"TRUE"),
|
|
],
|
|
)
|
|
def test_canonicalize_idempotent(text, ref, expected):
|
|
assert _canonicalize_via_kernel(text, ref) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"text,ref",
|
|
[
|
|
("the answer is 0.3", "arithmetic@v1"),
|
|
("approximately 0.3", "arithmetic@v1"),
|
|
("", "arithmetic@v1"),
|
|
(None, "arithmetic@v1"),
|
|
],
|
|
)
|
|
def test_canonicalize_rejects_non_canonical(text, ref):
|
|
assert _canonicalize_via_kernel(text, ref) is None
|
|
|
|
|
|
# ----- _classify_agreement ------------------------------------------------
|
|
|
|
|
|
def _mk_modality(name, ok, bytes_=None, error=None):
|
|
from arborist.qa.witness import ModalityResult
|
|
return ModalityResult(
|
|
modality=name,
|
|
raw_answer=bytes_.decode() if bytes_ else None,
|
|
canonical_bytes=bytes_ if ok else None,
|
|
error=error if not ok else None,
|
|
elapsed_ms=0.0,
|
|
)
|
|
|
|
|
|
def test_classify_strict_witnessed():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", True, b"3/10"),
|
|
"llm": _mk_modality("llm", True, b"3/10"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "STRICT-WITNESSED"
|
|
|
|
|
|
def test_classify_llm_diverged():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", True, b"3/10"),
|
|
"llm": _mk_modality("llm", True, b"2/5"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "LLM-DIVERGED"
|
|
|
|
|
|
def test_classify_cache_drift():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", True, b"4/10"),
|
|
"llm": _mk_modality("llm", True, b"3/10"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "CACHE-DRIFT"
|
|
|
|
|
|
def test_classify_kernel_llm_agree_no_cache():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", False, error="ABSENT"),
|
|
"llm": _mk_modality("llm", True, b"3/10"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "KERNEL-LLM-AGREE"
|
|
|
|
|
|
def test_classify_kernel_llm_diverged_no_cache():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", False, error="ABSENT"),
|
|
"llm": _mk_modality("llm", True, b"5/10"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "KERNEL-LLM-DIVERGED"
|
|
|
|
|
|
def test_classify_kernel_cache_agree_llm_absent():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", True, b"3/10"),
|
|
"llm": _mk_modality("llm", False, error="PIS_REJECT"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "KERNEL-CACHE-AGREE"
|
|
|
|
|
|
def test_classify_kernel_only():
|
|
mods = {
|
|
"kernel": _mk_modality("kernel", True, b"3/10"),
|
|
"cache": _mk_modality("cache", False, error="ABSENT"),
|
|
"llm": _mk_modality("llm", False, error="ABSENT"),
|
|
}
|
|
assert _classify_agreement(mods, b"3/10") == "KERNEL-ONLY"
|
|
|
|
|
|
# ----- run_witness end-to-end --------------------------------------------
|
|
|
|
|
|
def test_witness_kernel_llm_agree():
|
|
"""LLM emits the same canonical bytes — no cache."""
|
|
client = StubClient(answer="3/10")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
assert isinstance(w, Witness)
|
|
assert w.agreement_label == "KERNEL-LLM-AGREE"
|
|
assert w.modalities["kernel"].ok is True
|
|
assert w.modalities["cache"].error == "ABSENT"
|
|
assert w.modalities["llm"].ok is True
|
|
assert w.modalities["llm"].canonical_bytes == b"3/10"
|
|
assert w.modalities["llm"].raw_answer == "3/10"
|
|
|
|
|
|
def test_witness_llm_lexically_different_but_canonically_same():
|
|
"""LLM says '0.3'; kernel canonicalizes to '3/10' — agreement."""
|
|
client = StubClient(answer="0.3")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
assert w.agreement_label == "KERNEL-LLM-AGREE"
|
|
assert w.modalities["llm"].canonical_bytes == b"3/10"
|
|
|
|
|
|
def test_witness_llm_diverged():
|
|
"""LLM emits a wrong canonical-shape answer — divergence recorded."""
|
|
client = StubClient(answer="0.4")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
assert w.agreement_label == "KERNEL-LLM-DIVERGED"
|
|
assert w.modalities["llm"].canonical_bytes == b"2/5" # 0.4 → 2/5
|
|
assert w.modalities["llm"].raw_answer == "0.4"
|
|
|
|
|
|
def test_witness_llm_not_canonical_text():
|
|
"""LLM emits prose; kernel rejects → LLM modality absent."""
|
|
client = StubClient(answer="the answer is 0.3")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
# No cache, LLM rejected — kernel-only result.
|
|
assert w.agreement_label == "KERNEL-ONLY"
|
|
assert w.modalities["llm"].ok is False
|
|
assert w.modalities["llm"].error == "PIS_REJECT"
|
|
assert w.modalities["llm"].raw_answer == "the answer is 0.3"
|
|
|
|
|
|
def test_witness_cache_present_and_agrees():
|
|
"""Cache closure returns the canonical bytes — STRICT-WITNESSED."""
|
|
client = StubClient(answer="3/10")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
cache_lookup=lambda: b"3/10",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
assert w.agreement_label == "STRICT-WITNESSED"
|
|
assert w.modalities["cache"].ok is True
|
|
assert w.modalities["cache"].canonical_bytes == b"3/10"
|
|
|
|
|
|
def test_witness_cache_drift_caught():
|
|
"""Cache returns stale bytes; kernel + LLM agree → CACHE-DRIFT."""
|
|
client = StubClient(answer="3/10")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
cache_lookup=lambda: b"4/10", # someone bumped the kernel without invalidating
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
assert w.agreement_label == "CACHE-DRIFT"
|
|
assert w.modalities["cache"].canonical_bytes == b"2/5" # 4/10 → 2/5
|
|
|
|
|
|
def test_witness_no_chat_client_kernel_only():
|
|
"""No chat client provided — LLM modality reports ABSENT."""
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=None,
|
|
model_id="",
|
|
)
|
|
assert w.agreement_label == "KERNEL-ONLY"
|
|
assert w.modalities["llm"].error == "ABSENT"
|
|
|
|
|
|
def test_witness_llm_raises_recorded_as_error():
|
|
"""chat_completion raises — modality records LLM_ERROR, not crash."""
|
|
|
|
class RaisingClient:
|
|
def chat_completion(self, *a, **kw):
|
|
raise RuntimeError("connection refused")
|
|
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=RaisingClient(),
|
|
model_id="stub",
|
|
)
|
|
assert w.agreement_label == "KERNEL-ONLY"
|
|
assert w.modalities["llm"].error == "LLM_ERROR:RuntimeError"
|
|
|
|
|
|
def test_witness_to_dict_round_trip():
|
|
"""to_dict() emits a JSON-serializable shape."""
|
|
import json
|
|
|
|
client = StubClient(answer="3/10")
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
d = w.to_dict()
|
|
blob = json.dumps(d)
|
|
assert "STRICT" in blob or "AGREE" in blob
|
|
assert d["pi_star_ref"] == "arithmetic@v1"
|
|
assert d["canonical_answer_text"] == "3/10"
|
|
assert "kernel" in d["modalities"]
|
|
assert "llm" in d["modalities"]
|
|
|
|
|
|
def test_witness_parallel_not_sequential():
|
|
"""Total wall time ≤ slowest modality + overhead, not sum."""
|
|
|
|
class SlowClient:
|
|
def chat_completion(self, *a, **kw):
|
|
time.sleep(0.30)
|
|
return "3/10"
|
|
|
|
def slow_cache():
|
|
time.sleep(0.30)
|
|
return None
|
|
|
|
t0 = time.monotonic()
|
|
w = run_witness(
|
|
question="0.1 + 0.2",
|
|
pi_star_ref="arithmetic@v1",
|
|
canonical_answer_bytes=b"3/10",
|
|
cache_lookup=slow_cache,
|
|
chat_client=SlowClient(),
|
|
model_id="stub",
|
|
)
|
|
elapsed = time.monotonic() - t0
|
|
# Sequential would be ~0.60s; parallel should be ~0.30s + small overhead.
|
|
# Allow generous slack to avoid CI flake.
|
|
assert elapsed < 0.55, f"witness ran sequentially? wall={elapsed:.3f}s"
|
|
assert w.agreement_label == "KERNEL-LLM-AGREE"
|
|
|
|
|
|
# ----- query() integration ------------------------------------------------
|
|
|
|
|
|
def test_query_canonical_path_off_by_default(tmp_path):
|
|
"""Default policy: canonical_witness_enabled is False; no LLM call.
|
|
Persistence is also default-on (#000027), so first call writes
|
|
a row → status='cache_miss_then_written'."""
|
|
from arborist.qa.query import query
|
|
|
|
client = StubClient(answer="<should-not-be-called>")
|
|
result = query(
|
|
question="0.1 + 0.2",
|
|
qa_db=tmp_path / "qa.db",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
)
|
|
assert result["status"] == "cache_miss_then_written"
|
|
assert result["audit_mode"] == "CANONICAL_PROJECTION"
|
|
assert result.get("witness") is None
|
|
# Critical: no LLM round-trip happened.
|
|
assert client.calls == []
|
|
|
|
|
|
def test_query_canonical_with_witness_calls_llm(tmp_path):
|
|
"""policy['canonical_witness_enabled']=True → witness runs."""
|
|
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
|
|
|
|
client = StubClient(answer="3/10")
|
|
policy = dict(DEFAULT_QUERY_POLICY)
|
|
policy["canonical_witness_enabled"] = True
|
|
result = query(
|
|
question="0.1 + 0.2",
|
|
qa_db=tmp_path / "qa.db",
|
|
chat_client=client,
|
|
model_id="stub",
|
|
policy=policy,
|
|
)
|
|
assert result["status"] == "cache_miss_then_written"
|
|
witness = result.get("witness")
|
|
assert witness is not None
|
|
# First call: no prior cache row to compare against → cache leg
|
|
# is ABSENT; agreement is kernel↔LLM only.
|
|
assert witness["agreement_label"] == "KERNEL-LLM-AGREE"
|
|
# The LLM was actually called.
|
|
assert len(client.calls) == 1
|
|
|
|
|
|
def test_query_canonical_witness_reaches_strict_after_persist(tmp_path):
|
|
"""Post-#000027 + cache-leg wire: a SECOND witness call (after the
|
|
first writes a row) compares kernel + cache + LLM, all three
|
|
byte-equal → STRICT-WITNESSED. This was structurally unreachable
|
|
before #000027 landed — the cache leg always returned None."""
|
|
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
|
|
|
|
client = StubClient(answer="3/10")
|
|
policy = dict(DEFAULT_QUERY_POLICY)
|
|
policy["canonical_witness_enabled"] = True
|
|
qa_db = tmp_path / "qa.db"
|
|
|
|
# First call: persists row; cache leg ABSENT; KERNEL-LLM-AGREE.
|
|
first = query(
|
|
question="0.1 + 0.2", qa_db=qa_db,
|
|
chat_client=client, model_id="stub", policy=policy,
|
|
)
|
|
assert first["status"] == "cache_miss_then_written"
|
|
assert first["witness"]["agreement_label"] == "KERNEL-LLM-AGREE"
|
|
|
|
# Second call: cache hits (no LLM run from query() path; witness
|
|
# still calls LLM separately when enabled). Cache leg now
|
|
# populated with the persisted bytes → STRICT-WITNESSED.
|
|
second = query(
|
|
question="0.1 + 0.2", qa_db=qa_db,
|
|
chat_client=client, model_id="stub", policy=policy,
|
|
)
|
|
assert second["status"] == "cache_hit"
|
|
assert second["witness"]["agreement_label"] == "STRICT-WITNESSED"
|
|
|
|
|
|
# ---------- #000028 follow-ups: capital ledger + sample-rate -------------
|
|
|
|
|
|
def test_query_canonical_witness_sample_rate_zero_skips(tmp_path):
|
|
"""sample_rate=0.0 → witness never fires (passive-calibration
|
|
config); LLM is not called even when canonical_witness_enabled=True."""
|
|
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
|
|
|
|
client = StubClient(answer="3/10")
|
|
policy = dict(DEFAULT_QUERY_POLICY)
|
|
policy["canonical_witness_enabled"] = True
|
|
policy["canonical_witness_sample_rate"] = 0.0
|
|
qa_db = tmp_path / "qa.db"
|
|
result = query(
|
|
question="0.1 + 0.2", qa_db=qa_db,
|
|
chat_client=client, model_id="stub", policy=policy,
|
|
)
|
|
assert result["status"] == "cache_miss_then_written"
|
|
assert result.get("witness") is None # skipped — sampled out
|
|
assert client.calls == [] # critical: no LLM round-trip
|
|
|
|
|
|
def test_query_canonical_witness_sample_rate_one_always_fires(tmp_path):
|
|
"""sample_rate=1.0 (default when enabled) → witness fires every call."""
|
|
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
|
|
|
|
client = StubClient(answer="3/10")
|
|
policy = dict(DEFAULT_QUERY_POLICY)
|
|
policy["canonical_witness_enabled"] = True
|
|
policy["canonical_witness_sample_rate"] = 1.0
|
|
qa_db = tmp_path / "qa.db"
|
|
result = query(
|
|
question="0.1 + 0.2", qa_db=qa_db,
|
|
chat_client=client, model_id="stub", policy=policy,
|
|
)
|
|
assert result.get("witness") is not None
|
|
assert len(client.calls) == 1
|
|
|
|
|
|
def test_query_canonical_witness_capital_ledger_records_cost(tmp_path):
|
|
"""When witness fires + persistence writes a row, the witness LLM
|
|
cost lands in capital_ledger as op_type='canonical_witness'.
|
|
ForkScore can then compare witness-on vs witness-off forks
|
|
honestly via the ledger summary."""
|
|
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
|
|
from arborist.store import connect
|
|
|
|
client = StubClient(answer="3/10")
|
|
policy = dict(DEFAULT_QUERY_POLICY)
|
|
policy["canonical_witness_enabled"] = True
|
|
qa_db = tmp_path / "qa.db"
|
|
query(
|
|
question="0.1 + 0.2", qa_db=qa_db,
|
|
chat_client=client, model_id="stub", policy=policy,
|
|
)
|
|
conn = connect(qa_db)
|
|
try:
|
|
rows = conn.execute(
|
|
"SELECT op_type, estimator_inputs_blob FROM capital_ledger "
|
|
"WHERE op_type = 'canonical_witness'"
|
|
).fetchall()
|
|
finally:
|
|
conn.close()
|
|
assert len(rows) == 1
|
|
import json as _json
|
|
inputs = _json.loads(rows[0]["estimator_inputs_blob"])
|
|
assert inputs["pi_star_ref"] == "arithmetic@v1"
|
|
assert inputs["agreement_label"] in (
|
|
"STRICT-WITNESSED", "KERNEL-LLM-AGREE",
|
|
"CACHE-DRIFT", "LLM-DIVERGED", "KERNEL-LLM-DIVERGED",
|
|
)
|
|
|
|
|
|
def test_query_canonical_witness_no_ledger_when_sampled_out(tmp_path):
|
|
"""Sampled-out witness must not record capital cost (no work happened)."""
|
|
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
|
|
from arborist.store import connect
|
|
|
|
client = StubClient(answer="3/10")
|
|
policy = dict(DEFAULT_QUERY_POLICY)
|
|
policy["canonical_witness_enabled"] = True
|
|
policy["canonical_witness_sample_rate"] = 0.0
|
|
qa_db = tmp_path / "qa.db"
|
|
query(
|
|
question="0.1 + 0.2", qa_db=qa_db,
|
|
chat_client=client, model_id="stub", policy=policy,
|
|
)
|
|
conn = connect(qa_db)
|
|
try:
|
|
n = conn.execute(
|
|
"SELECT COUNT(*) FROM capital_ledger "
|
|
"WHERE op_type = 'canonical_witness'"
|
|
).fetchone()[0]
|
|
finally:
|
|
conn.close()
|
|
assert n == 0
|