Three small streams in one commit:
#000028 follow-up — witness divergence → 5F fixtures
=====================================================
Witness fan-out now writes a `providence_canonical_witness` audit
event when it fires (next to the capital-ledger record landed in
708aa45). Body carries pi_star_ref, question_text, agreement_label,
canonical_answer_text, llm_raw_text, llm_canonical_bytes,
cache_status. Best-effort write — chain failure never fails the
query.
New extractor `bench/scripts/witness_to_5f.py` reads those events
from a qa.db and writes them out as 5F-Falsification fixtures
matching the existing `falsification-live-v1` schema. Filtering
includes only divergence labels (LLM-DIVERGED / KERNEL-LLM-DIVERGED
/ CACHE-DRIFT); skips KERNEL-LLM-AGREE / STRICT-WITNESSED (no
calibration signal) and KERNEL-ONLY (LLM unparseable, not a
supervised-correction sample).
Idempotent: sorted by audit-event seq, so re-running against the
same qa.db produces byte-equal fixture files. The existing
fixture-digest discipline stays valid.
Makefile: `make bench-witness-divergence` (override default
qa.db / output path via WITNESS_QA_DB / WITNESS_OUT env-vars).
Closes the divergence → calibration data loop the witness ticket
imagined: every LLM hallucination on a canonical-shape question
becomes a supervised-correction fixture downstream prompt
improvements can grade against.
#000030 Phase 7 demo — function-sampled@v1 end-to-end
======================================================
`bench/scripts/demo_plot.py` — closes the loop on opencompletion's
activity24-math-plot.yaml. SymPy expression → quantized
integer-vector signature (canonical bytes) → optional matplotlib
PNG. Canonical bytes are the proof; PNG is just a downstream view
of the same evidence.
$ make demo-plot Q='sin(x)' PNG=/tmp/sin.png
Output JSON contains canonical_bytes_sha256 + canonical_bytes_preview
+ canonical_bytes_total_chars + grid metadata + the optional png_path.
matplotlib is gated — when absent, --png prints a warning to stderr
and skips the render; the canonical bytes still print. Tests skip
the PNG-presence assertion via `pytest.importorskip("matplotlib")`.
Public docs polish (#7)
========================
- docs/_source/bench.rst: updated fixture-count narrative (~660 →
662 default tasks + ~110 math π* fixtures); `make` quick-reference
now lists all per-π* 5S targets (tabular, calculus-limit/series,
linear-algebra, function-sampled) plus bench-real-shard,
bench-fork-baseline/score, bench-witness-divergence.
- docs/_source/v8-fork-score.rst: CLI section gained --out flag
documentation + a Make-harness sub-section covering
bench-fork-baseline / bench-fork-score / FORK_PARENT/CHILD/REPORT
env-vars.
Tests
=====
- tests/test_witness_to_5f.py — 8 new tests covering the audit-event
write (3) + extractor logic (5).
- tests/test_demo_plot.py — 6 new tests covering canonical-bytes
determinism + equivalence-class collapse + matplotlib gating.
Full suite: 1624 passed, 37 skipped (was 1568; +56).
192 lines
6.3 KiB
Python
192 lines
6.3 KiB
Python
"""Witness-divergence → 5F Falsification fixture extractor.
|
|
|
|
Pulls ``providence_canonical_witness`` audit events from a qa.db
|
|
and writes them out as 5F Falsification fixtures (matching the
|
|
existing ``falsification-live-v1`` schema). Each diverging witness
|
|
event becomes a supervised-correction sample the LLM can be
|
|
calibrated against — the kernel's canonical bytes are ground
|
|
truth, so an LLM raw answer that doesn't fold to those bytes is
|
|
falsifiable evidence of an arithmetic / logic / symbolic
|
|
hallucination.
|
|
|
|
#000028 follow-up — closes the divergence → calibration data loop.
|
|
The witness path landed in commit `656b573`; the audit-event write
|
|
landed in this same commit. The extractor here is the missing
|
|
piece that converts audit-chain entries into 5F-runner-compatible
|
|
fixtures.
|
|
|
|
Usage::
|
|
|
|
python -m bench.scripts.witness_to_5f \\
|
|
--qa-db ~/.arborist/shards/qa.db \\
|
|
--out bench/fixtures/5f/falsification-witness-v1.jsonl
|
|
|
|
Or via Makefile::
|
|
|
|
make bench-witness-divergence
|
|
|
|
Filtering rules
|
|
---------------
|
|
Includes events whose ``agreement_label`` indicates LLM divergence:
|
|
|
|
- ``LLM-DIVERGED`` (kernel + cache agree; LLM differs)
|
|
- ``KERNEL-LLM-DIVERGED`` (no cache; LLM differs from kernel)
|
|
- ``CACHE-DRIFT`` (kernel + LLM agree; cache differs — flagged
|
|
as a calibration-data point too since the cache row is wrong)
|
|
|
|
Skips:
|
|
|
|
- ``STRICT-WITNESSED`` / ``KERNEL-LLM-AGREE`` /
|
|
``KERNEL-CACHE-AGREE`` / ``KERNEL-ONLY`` — agreement, no signal.
|
|
- Events without ``llm_raw_text`` — LLM was unparseable; not a
|
|
useful supervised-correction sample.
|
|
|
|
Idempotency
|
|
-----------
|
|
Output is sorted by audit-event seq for deterministic byte-equality
|
|
across runs against the same qa.db. Re-running with no new
|
|
divergence events writes the same bytes. The fixture-digest
|
|
discipline (#000021 §3) stays valid.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from arborist.store import connect
|
|
|
|
|
|
_DIVERGENCE_LABELS = {
|
|
"LLM-DIVERGED",
|
|
"KERNEL-LLM-DIVERGED",
|
|
"CACHE-DRIFT",
|
|
}
|
|
|
|
|
|
def extract_divergence_fixtures(
|
|
qa_db: Path,
|
|
*,
|
|
fixture_id_prefix: str = "5f-fal-witness",
|
|
verifier_method_root: str = "verify_quotes-v1",
|
|
) -> list[dict]:
|
|
"""Pull divergence events from `qa_db`'s audit chain. Returns a
|
|
list of 5F-Falsification fixture dicts (sorted by audit-event
|
|
seq for determinism)."""
|
|
conn = connect(qa_db)
|
|
try:
|
|
rows = conn.execute(
|
|
"SELECT seq, body FROM audit_events "
|
|
"WHERE event_type = 'providence_canonical_witness' "
|
|
"ORDER BY seq ASC"
|
|
).fetchall()
|
|
finally:
|
|
conn.close()
|
|
|
|
fixtures: list[dict] = []
|
|
seen_ids: set[str] = set()
|
|
for r in rows:
|
|
try:
|
|
body = json.loads(r["body"])
|
|
except (json.JSONDecodeError, TypeError):
|
|
continue
|
|
agreement = body.get("agreement_label")
|
|
if agreement not in _DIVERGENCE_LABELS:
|
|
continue
|
|
llm_raw = body.get("llm_raw_text")
|
|
if not llm_raw:
|
|
# Unparseable LLM output isn't a calibration sample.
|
|
continue
|
|
canonical_text = body.get("canonical_answer_text") or ""
|
|
question_text = body.get("question_text") or ""
|
|
pi_star_ref = body.get("pi_star_ref") or "arithmetic@v1"
|
|
|
|
# Stable id from seq — re-running against the same qa.db
|
|
# produces the same fixture bytes (idempotency contract).
|
|
fixture_id = f"{fixture_id_prefix}-{int(r['seq']):06d}"
|
|
if fixture_id in seen_ids:
|
|
continue
|
|
seen_ids.add(fixture_id)
|
|
fixtures.append({
|
|
"id": fixture_id,
|
|
"battery": "5f",
|
|
"sub_battery": "falsification",
|
|
"version": "v1",
|
|
"carrier": "providence_record",
|
|
"domain": "claim_lattice",
|
|
"pi_star_ref": pi_star_ref,
|
|
"answer_text": llm_raw,
|
|
"context": (
|
|
f"canonical_kernel_answer={canonical_text}\n"
|
|
f"question={question_text}"
|
|
),
|
|
"expected_reason": "UNGROUNDED",
|
|
"verifier_method_root": verifier_method_root,
|
|
"expected": "pass",
|
|
"_witness_meta": {
|
|
"agreement_label": agreement,
|
|
"audit_seq": int(r["seq"]),
|
|
"pi_star_ref": pi_star_ref,
|
|
},
|
|
})
|
|
return fixtures
|
|
|
|
|
|
def write_fixtures(out_path: Path, fixtures: list[dict]) -> None:
|
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
meta = {
|
|
"_meta": {
|
|
"battery": "5f",
|
|
"sub_battery": "falsification",
|
|
"version": "v1",
|
|
"task_count": len(fixtures),
|
|
"notes": (
|
|
"Witness-divergence-extracted falsification fixtures. "
|
|
"Auto-generated by `make bench-witness-divergence` "
|
|
"from providence_canonical_witness audit events. "
|
|
"Each row pairs an LLM raw answer with the kernel's "
|
|
"canonical answer; verify_quotes is expected to "
|
|
"return UNGROUNDED because the LLM prose doesn't "
|
|
"substring-match the kernel's terse canonical form. "
|
|
"_witness_meta carries the original agreement_label "
|
|
"+ audit_seq for traceability."
|
|
),
|
|
},
|
|
}
|
|
with out_path.open("w", encoding="utf-8") as fh:
|
|
fh.write(json.dumps(meta, ensure_ascii=False) + "\n")
|
|
for fx in fixtures:
|
|
fh.write(json.dumps(fx, ensure_ascii=False) + "\n")
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
p = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
|
p.add_argument(
|
|
"--qa-db", type=Path, required=True,
|
|
help="path to qa.db (the providence_cache + audit_events store)",
|
|
)
|
|
p.add_argument(
|
|
"--out", type=Path,
|
|
default=Path("bench/fixtures/5f/falsification-witness-v1.jsonl"),
|
|
help="output JSONL path",
|
|
)
|
|
args = p.parse_args(argv)
|
|
|
|
if not args.qa_db.is_file():
|
|
print(f"error: qa-db not found: {args.qa_db}", file=sys.stderr)
|
|
return 2
|
|
|
|
fixtures = extract_divergence_fixtures(args.qa_db)
|
|
write_fixtures(args.out, fixtures)
|
|
print(
|
|
f"wrote {len(fixtures)} divergence fixtures to {args.out}",
|
|
file=sys.stderr,
|
|
)
|
|
print(json.dumps({"divergence_count": len(fixtures)}))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|