Two paired changes addressing fox's "learn this hyperparameter
from model use, not hard coding" + the 1M-context-window
caveat:
(1) Log-scale prompt-size buckets extend from 8KB through 1M+:
<8KB / 8-16KB / 16-32KB / 32-64KB / 64-128KB / 128-256KB /
256-512KB / 512K-1M / >=1M
The same bench harness now covers 8B-class models (Hermes 82K
context, max useful prompt ~32-64KB) through 1M-context models
(Gemini 1.5 Pro, Claude with extended context, Llama 4) without
code change. A model whose context window stops at 82K simply
never populates the giant buckets; a 1M-context model fills
them and finds its own sweet spot.
(2) New "recommended context budget (learned from this bench)"
section — per-mode peak-strict-rate bucket. Operator-driven
landing per the five-step algorithm step 5: surfaced, not
auto-applied. Minimum sample size of 5 runs per bucket so
statistical noise doesn't masquerade as signal. Tie-break on
smaller-bucket-wins so equivalent strict-rates favor the
cheaper choice.
The substrate is now self-tuning at the OBSERVATION layer: bench
records what budget actually grades best per model. Per-model
profile JSON (storing the recommended budget back into
~/.aborist/model_profiles/<model>.json) is the next beat once
this surface is observable in real bench runs.
3 new bench tests cover the recommended-budget section, the
sample-size floor, and the giant-context bucket coverage.
Full suite: 746 passed (was 738, +8).
Connects to D8 (automate after test-pinning): the bench tells us;
we don't guess.
369 lines
14 KiB
Python
369 lines
14 KiB
Python
"""Bench-side aggregation tests for capacity metrics + deflection rate.
|
|
|
|
The bench harness in ``bench/qa_sweep.py`` is the analytics surface for
|
|
QA-quality work — it consumes per-row ``prompt_chars_*`` / ``answer_chars``
|
|
fields from the query result and renders a markdown summary including
|
|
strict-rate-by-size buckets. These tests pin the aggregation behavior on
|
|
synthetic rows so the rendering doesn't drift silently.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def qa_sweep():
|
|
"""Import bench/qa_sweep.py as a module despite living outside the
|
|
aborist package. Avoids polluting the package or requiring a
|
|
pip-install of the bench harness."""
|
|
bench_path = Path(__file__).parent.parent / "bench" / "qa_sweep.py"
|
|
spec = importlib.util.spec_from_file_location("qa_sweep_under_test", bench_path)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = mod
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
def _row(**overrides) -> dict:
|
|
"""Synthetic bench-row fixture with sensible defaults for every
|
|
field the summarizer/renderer reads."""
|
|
base = {
|
|
"question": "q",
|
|
"answer_mode": "claim_lattice",
|
|
"status": "cache_miss_then_written",
|
|
"audit_mode": "STRICT",
|
|
"n_quotes": 1,
|
|
"n_verified": 1,
|
|
"ratio": 1.0,
|
|
"verifier_method": "claim_lattice",
|
|
"lookup_path": "miss",
|
|
"failure_stage": None,
|
|
"lazy_anchor_ratio": None,
|
|
"pointer_id_distribution": None,
|
|
"cache_key": "abc123",
|
|
"n_sources": 1,
|
|
"elapsed_s": 1.0,
|
|
"deflection_kind": "on_topic",
|
|
"subject_anchor": "x",
|
|
"subject_in_answer": True,
|
|
"prompt_chars_total": 5000,
|
|
"prompt_chars_evidence": 4000,
|
|
"prompt_chars_system": 800,
|
|
"prompt_chars_question": 30,
|
|
"answer_chars": 100,
|
|
"directive_compliance": {
|
|
"D2_pointer_clauses": True,
|
|
"D3_cti_substrate_ready": True,
|
|
"D4_evidence_map_bound": True,
|
|
"D6_warrant_fired": True,
|
|
"D7_honest_label": True,
|
|
},
|
|
"error": None,
|
|
}
|
|
base.update(overrides)
|
|
return base
|
|
|
|
|
|
def test_summarize_counts_verdicts_by_mode(qa_sweep):
|
|
rows = [
|
|
_row(answer_mode="quote", audit_mode="STRICT"),
|
|
_row(answer_mode="quote", audit_mode="HYBRID"),
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT"),
|
|
_row(answer_mode="claim_lattice", audit_mode="UNGROUNDED"),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
assert summary["quote"]["STRICT"] == 1
|
|
assert summary["quote"]["HYBRID"] == 1
|
|
assert summary["claim_lattice"]["STRICT"] == 1
|
|
assert summary["claim_lattice"]["UNGROUNDED"] == 1
|
|
|
|
|
|
def test_summarize_counts_deflections_only_on_grounded_rows(qa_sweep):
|
|
"""A deflection_kind=='deflection' on UNGROUNDED isn't a real
|
|
topic shift (no answer to deflect with). Only STRICT/HYBRID rows
|
|
with deflection_kind='deflection' should count toward the
|
|
deflections column."""
|
|
rows = [
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", deflection_kind="deflection"),
|
|
_row(answer_mode="claim_lattice", audit_mode="HYBRID", deflection_kind="deflection"),
|
|
_row(answer_mode="claim_lattice", audit_mode="UNGROUNDED", deflection_kind="deflection"),
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", deflection_kind="on_topic"),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
# 2 grounded deflections (STRICT + HYBRID); UNGROUNDED + on_topic don't count.
|
|
assert summary["claim_lattice"]["deflections"] == 2
|
|
|
|
|
|
def test_render_markdown_includes_summary_table_with_capacity_metrics(qa_sweep):
|
|
rows = [
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=5000),
|
|
_row(answer_mode="claim_lattice", audit_mode="HYBRID", prompt_chars_total=10000),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-01T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=2,
|
|
)
|
|
# Headline table has the deflections column (added before capacity work).
|
|
assert "deflections" in md
|
|
assert "strict-rate" in md
|
|
# Strict-rate-by-prompt-size section exists.
|
|
assert "strict-rate by prompt size" in md
|
|
# Buckets are present in the markdown body.
|
|
assert "<8KB" in md
|
|
assert "8-16KB" in md
|
|
|
|
|
|
def test_render_markdown_buckets_strict_rate_by_size(qa_sweep):
|
|
"""Two STRICT rows in <8KB bucket + two UNGROUNDED rows in
|
|
8-16KB bucket → <8KB strict-rate=1.0, 8-16KB strict-rate=0.0."""
|
|
rows = [
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=4000),
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=5000),
|
|
_row(answer_mode="claim_lattice", audit_mode="UNGROUNDED", prompt_chars_total=10000),
|
|
_row(answer_mode="claim_lattice", audit_mode="UNGROUNDED", prompt_chars_total=12000),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-01T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=4,
|
|
)
|
|
# Find the size-bucket section; verify both buckets show their
|
|
# expected strict-rate. Markdown table cell format is `| value |`.
|
|
bucket_section = md.split("strict-rate by prompt size")[1]
|
|
# <8KB row: 2 runs, 2 STRICT, rate 1.00
|
|
lines_8kb = [
|
|
L for L in bucket_section.splitlines()
|
|
if "<8KB" in L and "claim_lattice" in L
|
|
]
|
|
assert lines_8kb, f"missing <8KB row in:\n{bucket_section[:600]}"
|
|
assert " 1.00 " in lines_8kb[0]
|
|
# 8-16KB row: 2 runs, 0 STRICT, rate 0.00
|
|
lines_16kb = [
|
|
L for L in bucket_section.splitlines()
|
|
if "8-16KB" in L and "claim_lattice" in L
|
|
]
|
|
assert lines_16kb, f"missing 8-16KB row in:\n{bucket_section[:600]}"
|
|
assert " 0.00 " in lines_16kb[0]
|
|
|
|
|
|
def test_render_markdown_recommended_context_budget_section(qa_sweep):
|
|
"""Bench surfaces a per-mode peak-strict-rate bucket as the
|
|
'recommended context budget' — operator-readable, not auto-
|
|
applied. Minimum sample size of 5 runs/bucket; smaller samples
|
|
fall through as 'insufficient samples'."""
|
|
# 6 STRICT in <8KB bucket + 6 UNGROUNDED in 16-32KB bucket
|
|
# → <8KB has strict-rate 1.0, 16-32KB has 0.0; <8KB recommended.
|
|
rows = []
|
|
for _ in range(6):
|
|
rows.append(_row(
|
|
answer_mode="claim_lattice",
|
|
audit_mode="STRICT",
|
|
prompt_chars_total=4000,
|
|
))
|
|
for _ in range(6):
|
|
rows.append(_row(
|
|
answer_mode="claim_lattice",
|
|
audit_mode="UNGROUNDED",
|
|
prompt_chars_total=20000,
|
|
))
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-02T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=12,
|
|
)
|
|
assert "recommended context budget (learned from this bench)" in md
|
|
rec_section = md.split("recommended context budget")[1]
|
|
# Peak bucket is <8KB at 100%.
|
|
assert "<8KB" in rec_section
|
|
assert "1.00" in rec_section
|
|
# 16-32KB bucket (0% strict) does NOT show as the peak.
|
|
rec_table_lines = [
|
|
L for L in rec_section.splitlines()
|
|
if "claim_lattice" in L and "<8KB" in L
|
|
]
|
|
assert rec_table_lines, f"missing recommended-bucket row:\n{rec_section[:500]}"
|
|
|
|
|
|
def test_render_markdown_recommended_budget_skips_undersampled(qa_sweep):
|
|
"""Buckets with fewer than 5 runs don't qualify as
|
|
'recommended' — the recommendation needs statistical weight."""
|
|
# 3 STRICT in <8KB (under sample-size floor) + 3 STRICT in
|
|
# 16-32KB (also under floor). All buckets have <5 runs →
|
|
# 'insufficient samples' fallback.
|
|
rows = [
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=4000),
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=4000),
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=4000),
|
|
_row(answer_mode="claim_lattice", audit_mode="UNGROUNDED", prompt_chars_total=20000),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-02T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=4,
|
|
)
|
|
rec_section = md.split("recommended context budget")[1]
|
|
assert "insufficient samples" in rec_section
|
|
|
|
|
|
def test_render_markdown_log_scale_buckets_cover_giant_context(qa_sweep):
|
|
"""Log-scale buckets reach 1M+ chars so the same bench harness
|
|
covers 8B-class models (Hermes 82K context) through 1M-context
|
|
models (Gemini / Claude). A 500K-char prompt lands in the
|
|
512K-1M bucket without code change."""
|
|
rows = []
|
|
for _ in range(6):
|
|
rows.append(_row(
|
|
answer_mode="claim_lattice",
|
|
audit_mode="STRICT",
|
|
prompt_chars_total=600_000, # 600KB → 512K-1M bucket
|
|
))
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-02T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=6,
|
|
)
|
|
assert "512K-1M" in md
|
|
rec_section = md.split("recommended context budget")[1]
|
|
# 600K-char prompts land in the giant bucket; recommendation
|
|
# should pick it.
|
|
assert "512K-1M" in rec_section
|
|
|
|
|
|
def test_render_markdown_skips_empty_buckets(qa_sweep):
|
|
"""Bucket with zero runs in a mode shouldn't render an empty row
|
|
— the table should only include cells with data."""
|
|
rows = [
|
|
_row(answer_mode="claim_lattice", audit_mode="STRICT", prompt_chars_total=5000),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-01T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=1,
|
|
)
|
|
bucket_section = md.split("strict-rate by prompt size")[1]
|
|
# Only <8KB has data; >=64KB / 32-64KB / 16-32KB / 8-16KB rows
|
|
# should be absent.
|
|
for missing_label in (">=64KB", "32-64KB", "16-32KB", "8-16KB"):
|
|
assert missing_label not in bucket_section, (
|
|
f"empty bucket {missing_label} leaked into rendering:\n"
|
|
f"{bucket_section[:600]}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# directive coverage (seven-point program)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_summarize_aggregates_directive_pass_counts_per_mode(qa_sweep):
|
|
"""Per-row directive_compliance booleans aggregate into per-mode
|
|
pass counts. A 2-row sample with one D2 fail should report 1/2."""
|
|
rows = [
|
|
_row(
|
|
answer_mode="claim_lattice",
|
|
directive_compliance={
|
|
"D2_pointer_clauses": True,
|
|
"D3_cti_substrate_ready": True,
|
|
"D4_evidence_map_bound": True,
|
|
"D6_warrant_fired": True,
|
|
"D7_honest_label": True,
|
|
},
|
|
),
|
|
_row(
|
|
answer_mode="claim_lattice",
|
|
directive_compliance={
|
|
"D2_pointer_clauses": False, # the failing row
|
|
"D3_cti_substrate_ready": True,
|
|
"D4_evidence_map_bound": True,
|
|
"D6_warrant_fired": True,
|
|
"D7_honest_label": True,
|
|
},
|
|
),
|
|
]
|
|
summary = qa_sweep._summarize(rows)
|
|
dp = summary["claim_lattice"]["directive_pass"]
|
|
assert dp["D2_pointer_clauses"] == 1
|
|
assert dp["D3_cti_substrate_ready"] == 2
|
|
assert dp["D4_evidence_map_bound"] == 2
|
|
assert dp["D6_warrant_fired"] == 2
|
|
assert dp["D7_honest_label"] == 2
|
|
|
|
|
|
def test_render_markdown_directive_coverage_section(qa_sweep):
|
|
"""The markdown summary includes a 'directive coverage' table
|
|
showing per-mode pass counts per directive."""
|
|
rows = [_row(answer_mode="claim_lattice")]
|
|
summary = qa_sweep._summarize(rows)
|
|
md = qa_sweep._render_markdown(
|
|
rows, summary, "2026-05-01T00-00-00Z",
|
|
["claim_lattice"], ["q"], n_samples=1,
|
|
)
|
|
assert "directive coverage (seven-point program)" in md
|
|
# Header row contains the directive abbreviations.
|
|
assert "D2 pointer" in md
|
|
assert "D3 cti-ready" in md
|
|
assert "D4 ev-map bound" in md
|
|
assert "D6 warrant" in md
|
|
assert "D7 honest label" in md
|
|
# Body cell shows fraction (1/1) for the synthetic row.
|
|
coverage_section = md.split("directive coverage")[1]
|
|
# Per-mode row with 100% coverage on each directive.
|
|
assert "1/1 (100%)" in coverage_section
|
|
|
|
|
|
def test_directive_compliance_helper_marks_quote_mode_d2_false(qa_sweep):
|
|
"""Quote-mode rows fail D2 by definition (D2 demands lattice
|
|
answer mode)."""
|
|
result = {
|
|
"audit_mode": "STRICT",
|
|
"verifier_method": "quote",
|
|
"run_dag_root": None, # quote mode predates the run-DAG split
|
|
}
|
|
dc = qa_sweep._directive_compliance(
|
|
answer_mode="quote",
|
|
result=result,
|
|
err=None,
|
|
)
|
|
assert dc["D2_pointer_clauses"] is False
|
|
assert dc["D3_cti_substrate_ready"] is False # not lattice mode
|
|
assert dc["D4_evidence_map_bound"] is False # no run_dag_root
|
|
assert dc["D6_warrant_fired"] is False
|
|
# D7: quote-mode STRICT keeps the STRICT label; the four-rung
|
|
# ladder (POINTER-LINKED / ANCHOR-WARRANTED / EVIDENCE-WARRANTED)
|
|
# applies to lattice modes only. Quote mode passes D7 vacuously.
|
|
assert dc["D7_honest_label"] is True
|
|
|
|
|
|
def test_directive_compliance_helper_marks_lattice_mode_d2_true(qa_sweep):
|
|
"""Lattice-mode rows pass D2."""
|
|
result = {
|
|
"audit_mode": "STRICT",
|
|
"verifier_method": "claim_lattice",
|
|
"run_dag_root": "abc" * 21,
|
|
}
|
|
dc = qa_sweep._directive_compliance(
|
|
answer_mode="claim_lattice",
|
|
result=result,
|
|
err=None,
|
|
)
|
|
assert dc["D2_pointer_clauses"] is True
|
|
assert dc["D3_cti_substrate_ready"] is True
|
|
assert dc["D4_evidence_map_bound"] is True
|
|
assert dc["D6_warrant_fired"] is True
|
|
assert dc["D7_honest_label"] is True
|
|
|
|
|
|
def test_directive_compliance_returns_empty_on_error_row(qa_sweep):
|
|
"""Error rows don't carry directive signal — no per-row check
|
|
can succeed when the run threw."""
|
|
dc = qa_sweep._directive_compliance(
|
|
answer_mode="claim_lattice",
|
|
result={},
|
|
err="some error",
|
|
)
|
|
assert dc == {}
|