modified: .gitlab-ci.yml modified: bench/qa_questions.txt modified: bench/qa_sweep.py modified: bench/run.sh modified: docs/TICKETS.md modified: docs/_source/README.md modified: docs/_source/_ext/makefile_targets.py modified: docs/_source/api/cli.rst modified: docs/_source/api/distill.rst modified: docs/_source/api/mesh.rst modified: docs/_source/api/qa.rst modified: docs/_source/api/retrieval.rst modified: docs/_source/api/storage.rst modified: docs/_source/api/substrate.rst modified: docs/_source/concepts.rst modified: docs/_source/conf.py modified: docs/_source/cookbook.rst modified: docs/_source/index.rst modified: docs/_source/license.rst modified: docs/_source/quickstart.rst modified: docs/bench-maxing.md modified: docs/benchmarks.md modified: docs/cti-architecture.md modified: docs/diagrams/aborist-modules.dot modified: docs/diagrams/aborist-modules.svg modified: docs/diagrams/mesh-data-flow.dot modified: docs/diagrams/mesh-epoch-lifecycle.dot modified: docs/diagrams/mesh-epoch-lifecycle.svg modified: docs/diagrams/mesh-group-decisions.dot modified: docs/diagrams/mesh-group-decisions.svg modified: docs/diagrams/mesh-identity-stack.dot modified: docs/diagrams/mesh-secret-envelope.dot modified: docs/mesh.md modified: docs/qa-modes-bench.md modified: docs/seven-point-program.md modified: docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md modified: docs/tickets/ticket-000002-reference-frame-polarity-contract.md modified: docs/tickets/ticket-000003-anchor-class-warrant.md modified: docs/tickets/ticket-000005-label-ladder-migration.md modified: docs/tickets/ticket-000006-bench-emergent-findings.md modified: docs/tickets/ticket-000007-query-layer-hyphen-fold.md modified: docs/tickets/ticket-000008-broad-quantifier-preflight-guard.md modified: docs/tickets/ticket-000009-quantifier-preflight-dag-binding.md modified: docs/tickets/ticket-000010-metacognition-preflight-guard.md modified: docs/tickets/ticket-000011-soft-preflight-hint-sidecar.md modified: scripts/backfill_concepts.py modified: scripts/bench_emergent.py modified: tests/crawler/test_async_web_fetcher.py modified: tests/crawler/test_bridge.py modified: tests/crawler/test_web_fetch.py modified: tests/test_bench_qa_sweep.py modified: tests/test_burn.py modified: tests/test_burn_doc.py modified: tests/test_claim_lattice.py modified: tests/test_cli_render.py modified: tests/test_compress.py modified: tests/test_concepts.py modified: tests/test_dag.py modified: tests/test_directives.py modified: tests/test_distill.py modified: tests/test_distill_recursive.py modified: tests/test_evict.py modified: tests/test_frame.py modified: tests/test_grok_source.py modified: tests/test_html_source.py modified: tests/test_ingest.py modified: tests/test_inspect.py modified: tests/test_journal.py modified: tests/test_keys.py modified: tests/test_llm_context_base.py modified: tests/test_merkle.py modified: tests/test_mesh.py modified: tests/test_mesh_aead.py modified: tests/test_mesh_chain.py modified: tests/test_mesh_cli.py modified: tests/test_mesh_cli_pull.py modified: tests/test_mesh_wire.py modified: tests/test_mesh_wire_e2e.py modified: tests/test_metacognition.py modified: tests/test_migration_audit_mode.py modified: tests/test_providence_source.py modified: tests/test_qa.py modified: tests/test_qa_quality_live.py modified: tests/test_quantifier_caps.py modified: tests/test_quantifier_classifier.py modified: tests/test_quantifier_phase4.py modified: tests/test_quantifier_reminder.py modified: tests/test_query.py modified: tests/test_reclassify.py modified: tests/test_repair.py modified: tests/test_resume.py modified: tests/test_snapshot.py modified: tests/test_soft_preflight.py modified: tests/test_tfidf.py modified: tests/test_vcs_source.py modified: tests/test_verify.py modified: tests/test_verify_json.py modified: tests/test_versioned_ingest.py modified: tests/test_warrant.py modified: tests/test_wikipedia_old.py modified: tests/test_wikipedia_xml.py modified: tests/test_wikitext.py
14 KiB
Benchmarks — orientation, harnesses, fixtures, discipline
Arborist ships two bench harnesses, four question fixtures, and a signal-floor discipline pinned across the substrate. This doc is the canonical entry point. Three companion docs cover specifics:
| File | Purpose |
|---|---|
docs/bench-maxing.md |
Discipline rules (5pp signal floor, etc.) |
docs/qa-modes-bench.md |
Historical journal — running addendum log |
docs/bench-emergent-design.md |
Random-word stress-test design rationale |
docs/qa-modes-bench-2026-04-30.md |
Frozen point-in-time bench (Apr-30 baseline) |
Read those when you need the why or the long-form data. This file covers the what, where, and how.
1. Two harnesses, two purposes
1.1 bench/qa_sweep.py — curated QA-quality bench
Runs a fixture file × answer modes × n samples. Persists a JSONL of per-row results plus a markdown summary. Used for:
- Regression checks before merging substrate changes.
- A/B cycles on policy knobs (cap on/off, reminder on/off, preflight enabled/disabled).
- Per-mode strict-rate tracking over time.
Run via make bench-qa or bench/qa_sweep.py directly. Bench
rows carry ~25 fields per cell, including the four-rung label
(audit_mode), the verifier-method, the violation kinds, the
preflight QuestionState projection (preflight_logical_statuses,
preflight_question_shape, etc.), the quantifier classifier
output (quantifier_intensity, scope_bound_hint,
claim_cap_applied), the preflight stage hash 12-char prefix
(#000009 §7.2), and a per-stage timing breakdown
(preflight_ms, soft_preflight_ms, search_ms, context_ms,
llm_ms, persist_ms, total_ms).
1.2 scripts/bench_emergent.py — random-word stress test
Picks three random words from /usr/share/dict/words, asks
Hermes (at temp 0.8) to weave them into a creative question
paragraph, then sends that paragraph to arborist. Logs every cycle
to bench/emergent_log.jsonl as one JSONL line. Designed for
blue-moon cadence, not every-commit benching — the
combinatoric word space surfaces failure shapes the curated bench
doesn't reach.
The teacher review step is intentionally not automated. Fox brings interesting log entries to a teacher model (Claude Opus 4.7 in the current setup) and asks for guidance: did the answer match the question? Novelty class (known_truth_grounding / emergent_synthesis / novel_claim / no_signal)? Which hyperparam to tune next?
Run via make bench-emergent (default EMERGENT_N=10) or
make bench-emergent EMERGENT_N=100 for a longer cycle. See
docs/bench-emergent-design.md for the design rationale.
2. Four question fixtures
bench/qa_questions.txt — 75 questions
bench/qa_questions_smoke.txt — smoke test
bench/qa_questions_quantifier_subset.txt — 9 questions
bench/qa_questions_quantifier_baseline.txt — 1 question
bench/qa_questions_metacog_subset.txt — 28 questions
| Fixture | Use case | Cell size at n=3 × 3 modes |
|---|---|---|
qa_questions.txt |
Full regression bench. Run before merging substrate changes. Wide coverage + corner cases. | 675 runs, ~3-3.5h |
qa_questions_smoke.txt |
Smoke test on 1-2 questions. Use to confirm a CLI flag wires through correctly. | 3-6 runs, ~30s |
qa_questions_quantifier_subset.txt |
9 broad-quantifier questions (7 unbounded + 2 bounded). Use for A/B cycles on #000008 knobs. | 81 runs, ~7-15min |
qa_questions_quantifier_baseline.txt |
Single broad question for narrow n=3 baseline measurement. | 9 runs, ~2-4min |
qa_questions_metacog_subset.txt |
28 questions targeting each #000010 detector (temporal, contradiction, false-premise, out-of-corpus) plus mixed multi-trigger + well-formed controls. | 252 runs, ~25-35min |
When choosing a fixture: ask whether you're measuring the substrate as a whole (full bench), a specific feature (broad-quantifier subset, metacog subset), or just wiring correctness (smoke).
3. The signal floor — n=3 × 9 = 27 sample minimum, 5pp delta floor
Per docs/bench-maxing.md:
- 5pp floor for STRICT-rate deltas at n=3 × any-fixture sample size. Differences below this are Hermes nondeterminism, not signal. Quote-mode often jitters ±10pp on 27-sample cells.
- Mean-ratio + UNGROUNDED-rate are tighter signals at the same sample size — they aggregate per-row values rather than counting discrete verdicts.
- n=5 verification when an A/B cell sits within the noise band (e.g. 0.30 vs 0.33 STRICT-rate is 3pp — within noise; n=5 on the closer cell tightens the variance).
- vLLM concurrency saturates at c=3-4. Going higher slows
per-call latency without parallelizing harder. Default
c=4.
Don't tune to a single bench cycle. Always have a baseline + an A/B cell + (sometimes) a verification cell.
4. Make targets cheat sheet
# Curated QA bench (default: bench/qa_questions.txt × 3 modes × n=3)
make bench-qa
# Same but with limit / different file
.venv/bin/python bench/qa_sweep.py \
--questions bench/qa_questions_quantifier_subset.txt \
--shards-dir ~/.arborist/shards --n 3 --concurrency 4 \
--out-dir bench/qa_results
# A/B cycle: flip a policy knob for one bench cell
.venv/bin/python bench/qa_sweep.py \
--questions bench/qa_questions_quantifier_subset.txt \
--shards-dir ~/.arborist/shards --n 3 --concurrency 4 \
--policy quantifier_guard_apply_caps=true \
--policy quantifier_reminder_enabled=true
# Resume an interrupted bench (same --seed, append to existing JSONL)
.venv/bin/python bench/qa_sweep.py \
--resume bench/qa_results/2026-05-03T19-30-13Z.jsonl \
--seed 0
# Smoke test — quick sanity of the wiring
.venv/bin/python bench/qa_sweep.py \
--questions bench/qa_questions_smoke.txt --n 1 --concurrency 1
# Random-word stress test (blue-moon cadence)
make bench-emergent # n=10 default
make bench-emergent EMERGENT_N=100
5. The bench-row schema
Every row in bench/qa_results/<stamp>.jsonl carries (current
shape — fields are additive across versions):
Identity & verdict:
question,answer_mode,audit_mode,verifier_methodn_quotes,n_verified,ratiocache_key(12-char prefix)preflight_hash(12-char prefix; see #000009 §7.2)model_profile_id
Diagnostics:
format_collapsed(pointer-mode only signal; #000008)violation_kinds(sorted list of unique kinds)lazy_anchor_ratio,pointer_id_distributionanswer_brackets,answer_pointer_count,answer_chars_with_brackets,raw_meaningful_line_countdeflection_kind,subject_anchor,subject_in_answermetaphor_deflection_kind,metaphor_cue_count,metaphor_overlap_count
Preflight projection (#000010):
preflight_logical_statuses(list)preflight_question_shape,preflight_resultpreflight_temporal_sensitivitypreflight_has_false_premise,preflight_has_contradictionpreflight_corpus_requirement
Quantifier classifier (#000008):
quantifier_intensity,quantifier_matched_tokenscope_bound_hint,quantifier_explicit_countclaim_cap_applied
Capacity:
prompt_chars_total,prompt_chars_evidence,prompt_chars_system,prompt_chars_questionanswer_chars
Directive compliance (per-row pass/fail for the seven-point program directives observable from a single bench row):
directive_compliancedict keyed on D2/D3/D4/D6/D7
Time:
elapsed_s,iso_ts,error
The markdown summary alongside the JSONL renders aggregate views (per-mode strict-rate, format-collapse + violation kinds table, strict-rate-by-prompt-size buckets, directive coverage).
6. The journal — where headlines live
docs/qa-modes-bench.md is the rolling research log for the
QA bench. Each substantial bench cycle adds a new addendum (or
refines an existing one). The 2026-05-02 baseline is the frozen
authoritative state of the substrate before #000008/9/10/11
landed; subsequent addenda compare against it.
Current addenda:
- 2026-05-02 baseline — pre-preflight authoritative state.
- Addendum 1 (broad-quantifier A/B from #000008 §12.6).
- Addendum 2 (preflight on vs off, 9-question broad subset).
- Addendum 3 (full 75-question regression check post-flip).
- Addendum 4 (metacog-trigger detector validation, 6 → 28 question fixture).
When a new bench cycle produces a substantive finding, add an
addendum or refine an existing one. Bench artifacts (JSONL +
markdown) live in bench/qa_results/ and are gitignored — only
headlines + cross-references go in the journal.
Per-ticket bench data lives in the relevant ticket file's §12
or §13 (e.g. docs/tickets/ticket-000008-...md §12 carries the
four-cell A/B; #000010 §13 has three sub-validations; #000006 has
the rolling-amend pattern across multiple cycle counts).
7. The bench-emergent log
bench/emergent_log.jsonl is a long-running append-only log of
random-word stress cycles. Each entry has:
{
"ts": <unix>, "iso_ts": "2026-05-04T...",
"words": ["dismally", "heats", "Jamaicans"],
"question": "<model-generated question paragraph>",
"answer": "<arborist's answer>",
"audit_mode": "STRICT|HYBRID|UNGROUNDED",
"verifier_method": "claim_lattice",
"n_quotes": 3, "n_verified": 0,
"violation_kinds": ["CITATION_MISMATCH", "TOO_MANY_EVIDENCE_IDS"],
"metaphor_deflection_kind": "no_signal",
"metaphor_cue_count": 2, "metaphor_overlap_count": 2,
"sources": [{"title": "...", "uri": "...", "used": false}, ...],
"answer_seconds": <float>, "total_seconds": <float>,
"teacher": <reserved for human review>
}
Findings live in docs/tickets/ticket-000006-bench-emergent-findings.md
as rolling amends — each new cycle batch produces a new amend with
the verdict shift, violation profile, and tuning candidates.
8. How to run a focused A/B
Pattern from #000008 §12 (broad-quantifier four-cell A/B):
- Pick a fixture matching the feature under test (broad-subset for quantifier work; metacog-subset for metacognition work; smoke for wiring sanity).
- Run the baseline — same fixture, default policy.
- Run the cell — same fixture, one policy knob flipped via
--policy KEY=VALUE. Repeat for each independent knob. - Compare — STRICT-rate, mean-ratio, UNGROUNDED-rate, FORMAT_COLLAPSED rate, dominant violation kinds. Apply the 5pp floor to STRICT-rate; mean-ratio and UNGROUNDED tend to be tighter.
- Verify with n=5 if a cell sits within the noise band.
- Document — add an addendum to
docs/qa-modes-bench.mdor a new§12.x/§13.xto the relevant ticket file.
The four-cell A/B in #000008 (preflight off / reminder only / cap only / cap+reminder) is the canonical pattern for measuring multi-knob features.
9. How to interpret results
STRICT-rate = the fraction of cells where the verifier could prove every claim grounded against cited evidence. Most direct quality metric, but noisy at small sample sizes.
HYBRID-rate = mixed verdicts (some claims grounded, some not). Often more informative than STRICT-rate when a feature's job is to rescue UNGROUNDED → HYBRID rather than HYBRID → STRICT.
UNGROUNDED-rate = honest "the verifier could not ground."
Lower is generally better, but UNGROUNDED on questions the corpus
genuinely can't answer is the correct outcome — the substrate
preferring UNGROUNDED to fabricated STRICT is the architectural
win named in docs/seven-point-program.md D7.
Mean ratio = mean of n_verified / n_quotes across rows.
Aggregates per-row values rather than counting verdicts; tighter
at small sample sizes than STRICT-rate.
FORMAT_COLLAPSED rate = pointer-mode-specific gauge of
whether the model is following the [E\d+] citation protocol.
Zero is the goal; non-zero means the model emitted free prose
without pointer tags.
Violation kind distribution = per-mode tallies of which hard checks fired most often. Gives the operator a per-failure-mode view of the substrate's weaknesses.
Directive compliance = per-row pass/fail for the seven-point-program directives (D2/D3/D4/D6/D7). Substrate-level hygiene check; should be ~100% on lattice modes.
Audit-line tails (rendered by
arborist/cli.py:_render_warrant_tail):
- Hard tails:
· warrant missing,· title mismatch,· format collapsed,· broad cap N,· broad rejected,· broad unbounded,· broad runaway,· false premise,· contradictory,· stale risk,· out of corpus,· frame ambiguous. - Soft tails (advisory):
· soft: <label>(#000011 sidecar output; SOFT_DISABLED / SOFT_PARSE_FAIL / SOFT_WELL_FORMED suppress).
A row with multiple tails composes them: EVIDENCE-WARRANTED · via claim_lattice · false premise · soft: time sensitive is a
verdict the substrate reached but with two distinct preflight
warnings worth the operator's attention.
10. Operator commands cheat sheet
# Inspect preflight stage payload from any cache row
arborist providence --show-preflight CACHE_KEY_PREFIX
# Single query with all preflight knobs flipped
arborist query "winners of all major sports?" \
--apply-quantifier-caps --reject-broad --soft-preflight
# Dry-run — assemble context but skip LLM
arborist query "..." --dry-run
# Force fresh inference (delete prior cache row first)
arborist query "..." --burn
# Show raw JSON record instead of human render
arborist query "..." --json
The Makefile exposes most of these via shorthand:
make query Q="..." BROAD=1 REJECT_BROAD=1 ALLOW_BROAD=1 \
BURN=1 JSON=1 ANSWER_MODE=claim_lattice
See the Makefile directly for the full set.