From 8d6961fcc1a0533ecdeea1855d0a394878593310 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Thu, 7 May 2026 09:31:49 -0400 Subject: [PATCH] aborist/arborist 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 --- .gitlab-ci.yml | 4 +- bench/qa_questions.txt | 2 +- bench/qa_sweep.py | 22 ++--- bench/run.sh | 14 +-- docs/TICKETS.md | 4 +- docs/_source/README.md | 2 +- docs/_source/_ext/makefile_targets.py | 2 +- docs/_source/api/cli.rst | 4 +- docs/_source/api/distill.rst | 2 +- docs/_source/api/mesh.rst | 4 +- docs/_source/api/qa.rst | 18 ++-- docs/_source/api/retrieval.rst | 10 +- docs/_source/api/storage.rst | 6 +- docs/_source/api/substrate.rst | 6 +- docs/_source/concepts.rst | 14 +-- docs/_source/conf.py | 8 +- docs/_source/cookbook.rst | 14 +-- docs/_source/index.rst | 2 +- docs/_source/license.rst | 2 +- docs/_source/quickstart.rst | 20 ++-- docs/bench-maxing.md | 2 +- docs/benchmarks.md | 22 ++--- docs/cti-architecture.md | 52 +++++----- docs/diagrams/aborist-modules.dot | 6 +- docs/diagrams/aborist-modules.svg | 4 +- docs/diagrams/mesh-data-flow.dot | 4 +- docs/diagrams/mesh-epoch-lifecycle.dot | 10 +- docs/diagrams/mesh-epoch-lifecycle.svg | 8 +- docs/diagrams/mesh-group-decisions.dot | 10 +- docs/diagrams/mesh-group-decisions.svg | 8 +- docs/diagrams/mesh-identity-stack.dot | 2 +- docs/diagrams/mesh-secret-envelope.dot | 2 +- docs/mesh.md | 40 ++++---- docs/qa-modes-bench.md | 6 +- docs/seven-point-program.md | 34 +++---- ...ket-000001-retrieval-keywords-audit-gap.md | 28 +++--- ...00002-reference-frame-polarity-contract.md | 6 +- .../ticket-000003-anchor-class-warrant.md | 6 +- .../ticket-000005-label-ladder-migration.md | 6 +- .../ticket-000006-bench-emergent-findings.md | 6 +- .../ticket-000007-query-layer-hyphen-fold.md | 10 +- ...000008-broad-quantifier-preflight-guard.md | 98 +++++++++---------- ...000009-quantifier-preflight-dag-binding.md | 14 +-- ...et-000010-metacognition-preflight-guard.md | 14 +-- ...cket-000011-soft-preflight-hint-sidecar.md | 26 ++--- scripts/backfill_concepts.py | 14 +-- scripts/bench_emergent.py | 30 +++--- tests/crawler/test_async_web_fetcher.py | 2 +- tests/crawler/test_bridge.py | 36 +++---- tests/crawler/test_web_fetch.py | 14 +-- tests/test_bench_qa_sweep.py | 2 +- tests/test_burn.py | 20 ++-- tests/test_burn_doc.py | 24 ++--- tests/test_claim_lattice.py | 62 ++++++------ tests/test_cli_render.py | 6 +- tests/test_compress.py | 14 +-- tests/test_concepts.py | 12 +-- tests/test_dag.py | 18 ++-- tests/test_directives.py | 46 ++++----- tests/test_distill.py | 14 +-- tests/test_distill_recursive.py | 14 +-- tests/test_evict.py | 16 +-- tests/test_frame.py | 2 +- tests/test_grok_source.py | 8 +- tests/test_html_source.py | 2 +- tests/test_ingest.py | 12 +-- tests/test_inspect.py | 22 ++--- tests/test_journal.py | 16 +-- tests/test_keys.py | 12 +-- tests/test_llm_context_base.py | 14 +-- tests/test_merkle.py | 6 +- tests/test_mesh.py | 10 +- tests/test_mesh_aead.py | 16 +-- tests/test_mesh_chain.py | 12 +-- tests/test_mesh_cli.py | 22 ++--- tests/test_mesh_cli_pull.py | 16 +-- tests/test_mesh_wire.py | 30 +++--- tests/test_mesh_wire_e2e.py | 14 +-- tests/test_metacognition.py | 28 +++--- tests/test_migration_audit_mode.py | 2 +- tests/test_providence_source.py | 10 +- tests/test_qa.py | 18 ++-- tests/test_qa_quality_live.py | 24 ++--- tests/test_quantifier_caps.py | 18 ++-- tests/test_quantifier_classifier.py | 2 +- tests/test_quantifier_phase4.py | 4 +- tests/test_quantifier_reminder.py | 4 +- tests/test_query.py | 48 ++++----- tests/test_reclassify.py | 16 +-- tests/test_repair.py | 18 ++-- tests/test_resume.py | 10 +- tests/test_snapshot.py | 10 +- tests/test_soft_preflight.py | 2 +- tests/test_tfidf.py | 14 +-- tests/test_vcs_source.py | 8 +- tests/test_verify.py | 4 +- tests/test_verify_json.py | 24 ++--- tests/test_versioned_ingest.py | 8 +- tests/test_warrant.py | 2 +- tests/test_wikipedia_old.py | 2 +- tests/test_wikipedia_xml.py | 10 +- tests/test_wikitext.py | 4 +- 102 files changed, 716 insertions(+), 716 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d3a273e..225855c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# GitLab CI for aborist. +# GitLab CI for arborist. # # Runs the unit test suite on every push. Lives at the same shape as # the sibling repos (`unsandbox.com`, `unfirehose-nextjs-logger`): @@ -7,7 +7,7 @@ # # What's NOT in CI: # - `make test-live` — needs the live Hermes endpoint + populated -# shards under ~/.aborist/shards. Run by hand via `make test-live` +# shards under ~/.arborist/shards. Run by hand via `make test-live` # when iterating on QA quality. # - `make test-crawler` — needs `[crawler]` extras + network access # to real HTML sites. Opt-in via `make test-crawler` locally. diff --git a/bench/qa_questions.txt b/bench/qa_questions.txt index a7c1154..b04555b 100644 --- a/bench/qa_questions.txt +++ b/bench/qa_questions.txt @@ -1,4 +1,4 @@ -# aborist QA-quality benchmark question set. +# arborist QA-quality benchmark question set. # # One question per line. `#`-prefixed lines and blank lines ignored. # Designed for the 2003-05-16 Wikipedia cur snapshot — questions must diff --git a/bench/qa_sweep.py b/bench/qa_sweep.py index 299f0cd..c877bbb 100644 --- a/bench/qa_sweep.py +++ b/bench/qa_sweep.py @@ -1,6 +1,6 @@ """QA-quality benchmark sweep. -Runs a fixed question set through ``aborist.qa.query.query`` under each +Runs a fixed question set through ``arborist.qa.query.query`` under each answer mode and tabulates STRICT / HYBRID / UNGROUNDED counts, the ``n_verified / n_quotes`` ratio, latency, and lazy-anchor signals. @@ -36,7 +36,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from threading import Lock -# Defer aborist imports until argparse runs so `--help` works without +# Defer arborist imports until argparse runs so `--help` works without # the package installed. ANSWER_MODES = ("quote", "claim_lattice_pointer", "claim_lattice") @@ -134,19 +134,19 @@ def _run_one( can flip individual fields for an A/B cycle without touching the policy defaults. """ - from aborist.qa.client import OpenAICompatibleClient - from aborist.qa.query import DEFAULT_QUERY_POLICY, query + from arborist.qa.client import OpenAICompatibleClient + from arborist.qa.query import DEFAULT_QUERY_POLICY, query policy = dict(DEFAULT_QUERY_POLICY) policy["answer_mode"] = answer_mode if policy_overrides: policy.update(policy_overrides) - api_key = os.environ.get("ABORIST_LLM_API_KEY") + api_key = os.environ.get("ARBORIST_LLM_API_KEY") client = OpenAICompatibleClient(base_url=endpoint, api_key=api_key) if qa_db is None: - qa_db = (shards_dir / "qa.db") if shards_dir else (Path.home() / ".aborist" / "qa.db") + qa_db = (shards_dir / "qa.db") if shards_dir else (Path.home() / ".arborist" / "qa.db") t0 = time.monotonic() err: str | None = None @@ -171,7 +171,7 @@ def _run_one( # never mentions the question's subject) at bench-aggregate # scale so a creeping "model deflects rather than refuses" # regression is legible across runs. - from aborist.qa.inspect import diagnose_deflection, diagnose_metaphor_deflection + from arborist.qa.inspect import diagnose_deflection, diagnose_metaphor_deflection deflection = diagnose_deflection(question, result.get("answer_text") or "") metaphor = diagnose_metaphor_deflection( question, result.get("answer_text") or "" @@ -493,7 +493,7 @@ def _render_markdown( ) -> str: lines: list[str] = [] n_runs = len(rows) - lines.append(f"# aborist QA-quality benchmark — {started_utc}") + lines.append(f"# arborist QA-quality benchmark — {started_utc}") lines.append("") lines.append( f"questions: {len(questions)} · modes: {len(modes)} · samples per cell: " @@ -730,7 +730,7 @@ def _render_markdown( def main(argv: list[str] | None = None) -> int: ap = argparse.ArgumentParser(description=__doc__) ap.add_argument("--questions", type=Path, default=Path("bench/qa_questions.txt")) - ap.add_argument("--shards-dir", type=Path, default=Path.home() / ".aborist" / "shards") + ap.add_argument("--shards-dir", type=Path, default=Path.home() / ".arborist" / "shards") ap.add_argument("--qa-db", type=Path, default=None) ap.add_argument("--out-dir", type=Path, default=Path("bench/qa_results")) ap.add_argument("--top-k", type=int, default=8) @@ -765,9 +765,9 @@ def main(argv: list[str] | None = None) -> int: "re-rendered from the union of pre-existing + " "new rows. Stop/start-able bench.") ap.add_argument("--endpoint", default=os.environ.get( - "ABORIST_LLM_ENDPOINT", "https://hermes.ai.unturf.com/v1")) + "ARBORIST_LLM_ENDPOINT", "https://hermes.ai.unturf.com/v1")) ap.add_argument("--model", default=os.environ.get( - "ABORIST_LLM_MODEL", "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic")) + "ARBORIST_LLM_MODEL", "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic")) ap.add_argument( "--policy", action="append", default=None, metavar="KEY=VALUE", help=( diff --git a/bench/run.sh b/bench/run.sh index 8fba13c..d3fb844 100755 --- a/bench/run.sh +++ b/bench/run.sh @@ -12,10 +12,10 @@ set -uo pipefail BENCH_DOCS=${1:-5000} -BENCH_DIR=${BENCH_DIR:-/tmp/aborist-bench} +BENCH_DIR=${BENCH_DIR:-/tmp/arborist-bench} SHARDS=${SHARDS:-4} DUMP=${DUMP:-data/20030516_cur_tablesql.bz2} -ABORIST=${ABORIST:-.venv/bin/aborist} +ARBORIST=${ARBORIST:-.venv/bin/arborist} mkdir -p "$BENCH_DIR" @@ -40,7 +40,7 @@ bench_serial() { local db=$BENCH_DIR/serial.db rm -f "$db" "$db-"* local wall - wall=$(run_silent "$ABORIST" --db "$db" ingest --quiet \ + wall=$(run_silent "$ARBORIST" --db "$db" ingest --quiet \ --source wikipedia_cur --path "$DUMP" --limit "$BENCH_DOCS") local docs docs=$(sqlite3 "$db" "SELECT COUNT(*) FROM documents") @@ -54,7 +54,7 @@ bench_parallel_shared() { local start end wall start=$(date +%s.%N) for i in $(seq 0 $((SHARDS - 1))); do - "$ABORIST" --db "$db" ingest --quiet \ + "$ARBORIST" --db "$db" ingest --quiet \ --source wikipedia_cur --path "$DUMP" \ --shard "$i/$SHARDS" --limit "$per" >/dev/null 2>&1 & done @@ -74,7 +74,7 @@ bench_attached() { local start end wall start=$(date +%s.%N) for i in $(seq 0 $((SHARDS - 1))); do - "$ABORIST" ingest --quiet \ + "$ARBORIST" ingest --quiet \ --source wikipedia_cur --path "$DUMP" \ --shards-dir "$dir" --shard "$i/$SHARDS" --limit "$per" >/dev/null 2>&1 & done @@ -82,12 +82,12 @@ bench_attached() { end=$(date +%s.%N) wall=$(echo "$end - $start" | bc -l) local docs - docs=$("$ABORIST" --shards-dir "$dir" stats 2>/dev/null \ + docs=$("$ARBORIST" --shards-dir "$dir" stats 2>/dev/null \ | python3 -c "import json,sys; print(json.load(sys.stdin)['documents_total'])") printf "attached,%s,%s\n" "$wall" "$docs" } -echo "=== aborist ETL benchmark — $BENCH_DOCS docs target, $SHARDS shards ===" +echo "=== arborist ETL benchmark — $BENCH_DOCS docs target, $SHARDS shards ===" echo results+=("$(bench_serial)") diff --git a/docs/TICKETS.md b/docs/TICKETS.md index de8d5bf..dbbcc06 100644 --- a/docs/TICKETS.md +++ b/docs/TICKETS.md @@ -1,6 +1,6 @@ -# Aborist tickets — index +# Arborist tickets — index -Aborist uses a flat `ticket-NNNNNN-.md` convention for actionable +Arborist uses a flat `ticket-NNNNNN-.md` convention for actionable proposals: defects with a known root cause, design proposals awaiting go/no-go, or scoped enhancements queued for future implementation. diff --git a/docs/_source/README.md b/docs/_source/README.md index dda2711..45f954c 100644 --- a/docs/_source/README.md +++ b/docs/_source/README.md @@ -1,4 +1,4 @@ -# Aborist API Reference (Sphinx) +# Arborist API Reference (Sphinx) This directory contains Sphinx configuration to generate API documentation from docstrings. diff --git a/docs/_source/_ext/makefile_targets.py b/docs/_source/_ext/makefile_targets.py index 16274ef..1c09b13 100644 --- a/docs/_source/_ext/makefile_targets.py +++ b/docs/_source/_ext/makefile_targets.py @@ -139,7 +139,7 @@ def generate_rst(all_targets: dict[str, str], output_path: Path) -> None: "Makefile reference", "==================", "", - "Every aborist workflow lives behind a ``make`` target. This page is", + "Every arborist workflow lives behind a ``make`` target. This page is", "auto-generated from the project ``Makefile``'s ``## description``", "annotations at Sphinx build time, so it stays in sync with the source.", "", diff --git a/docs/_source/api/cli.rst b/docs/_source/api/cli.rst index b1a17cb..42bfec9 100644 --- a/docs/_source/api/cli.rst +++ b/docs/_source/api/cli.rst @@ -1,8 +1,8 @@ CLI: Command-line interface ============================ -Entry point for all aborist operations. +Entry point for all arborist operations. -.. automodule:: aborist.cli +.. automodule:: arborist.cli :members: :undoc-members: diff --git a/docs/_source/api/distill.rst b/docs/_source/api/distill.rst index e8597b6..7bf0217 100644 --- a/docs/_source/api/distill.rst +++ b/docs/_source/api/distill.rst @@ -3,7 +3,7 @@ Distillation: surface → core compression Surface-to-core extraction and recursive distillation. -.. automodule:: aborist.distill +.. automodule:: arborist.distill :members: :undoc-members: :show-inheritance: diff --git a/docs/_source/api/mesh.rst b/docs/_source/api/mesh.rst index d190ec8..934da7c 100644 --- a/docs/_source/api/mesh.rst +++ b/docs/_source/api/mesh.rst @@ -1,9 +1,9 @@ -Federation: multiplayer aborist +Federation: multiplayer arborist ================================ Gossip-based mesh for cross-peer data sharing. -.. automodule:: aborist.mesh +.. automodule:: arborist.mesh :members: :undoc-members: :show-inheritance: diff --git a/docs/_source/api/qa.rst b/docs/_source/api/qa.rst index 536a794..0a51d50 100644 --- a/docs/_source/api/qa.rst +++ b/docs/_source/api/qa.rst @@ -6,62 +6,62 @@ Question answering, caching, verification, evidence mapping. keys ---- -.. automodule:: aborist.qa.keys +.. automodule:: arborist.qa.keys :members: :undoc-members: runner ------ -.. automodule:: aborist.qa.runner +.. automodule:: arborist.qa.runner :members: :undoc-members: query ----- -.. automodule:: aborist.qa.query +.. automodule:: arborist.qa.query :members: :undoc-members: verify ------ -.. automodule:: aborist.qa.verify +.. automodule:: arborist.qa.verify :members: :undoc-members: evidence -------- -.. automodule:: aborist.qa.evidence +.. automodule:: arborist.qa.evidence :members: :undoc-members: quantifier ---------- -.. automodule:: aborist.qa.quantifier +.. automodule:: arborist.qa.quantifier :members: :undoc-members: metacognition ------------- -.. automodule:: aborist.qa.metacognition +.. automodule:: arborist.qa.metacognition :members: :undoc-members: dag --- -.. automodule:: aborist.qa.dag +.. automodule:: arborist.qa.dag :members: :undoc-members: client ------ -.. automodule:: aborist.qa.client +.. automodule:: arborist.qa.client :members: :undoc-members: diff --git a/docs/_source/api/retrieval.rst b/docs/_source/api/retrieval.rst index 2ca3cfa..1b5bbd5 100644 --- a/docs/_source/api/retrieval.rst +++ b/docs/_source/api/retrieval.rst @@ -6,7 +6,7 @@ Full-text search, concept relations (synonym/rivalry overlay). search ------ -.. automodule:: aborist.search +.. automodule:: arborist.search :members: :undoc-members: :show-inheritance: @@ -14,22 +14,22 @@ search sources ------- -.. automodule:: aborist.source +.. automodule:: arborist.source :members: :undoc-members: -.. automodule:: aborist.sources.wikipedia +.. automodule:: arborist.sources.wikipedia :members: :undoc-members: -.. automodule:: aborist.sources.html_page +.. automodule:: arborist.sources.html_page :members: :undoc-members: concepts -------- -.. automodule:: aborist.concepts +.. automodule:: arborist.concepts :members: :undoc-members: :show-inheritance: diff --git a/docs/_source/api/storage.rst b/docs/_source/api/storage.rst index 17a90b5..4c7aa47 100644 --- a/docs/_source/api/storage.rst +++ b/docs/_source/api/storage.rst @@ -6,7 +6,7 @@ v9.8 SQLite schema, audit chain, and cross-shard views. store ----- -.. automodule:: aborist.store +.. automodule:: arborist.store :members: :undoc-members: :show-inheritance: @@ -14,7 +14,7 @@ store ingest ------ -.. automodule:: aborist.ingest +.. automodule:: arborist.ingest :members: :undoc-members: :show-inheritance: @@ -22,6 +22,6 @@ ingest evict ----- -.. automodule:: aborist.evict +.. automodule:: arborist.evict :members: :undoc-members: diff --git a/docs/_source/api/substrate.rst b/docs/_source/api/substrate.rst index 7007f6f..27b67db 100644 --- a/docs/_source/api/substrate.rst +++ b/docs/_source/api/substrate.rst @@ -6,7 +6,7 @@ Pure Merkle tree and document primitives. merkle ------ -.. automodule:: aborist.merkle +.. automodule:: arborist.merkle :members: :undoc-members: :show-inheritance: @@ -14,7 +14,7 @@ merkle document -------- -.. automodule:: aborist.document +.. automodule:: arborist.document :members: :undoc-members: :show-inheritance: @@ -22,6 +22,6 @@ document wikitext -------- -.. automodule:: aborist.wikitext +.. automodule:: arborist.wikitext :members: :undoc-members: diff --git a/docs/_source/concepts.rst b/docs/_source/concepts.rst index efc7b1f..aac6597 100644 --- a/docs/_source/concepts.rst +++ b/docs/_source/concepts.rst @@ -1,12 +1,12 @@ Concepts ======== -Aborist is a content-addressed document store that gives every cached +Arborist is a content-addressed document store that gives every cached LLM answer a verifiable Merkle proof tying it back to its source documents. This page is the orientation: what the system is, the core abstractions you'll see in code and docs, and how they compose. -What aborist is +What arborist is --------------- A reference implementation of two papers stacked: @@ -26,8 +26,8 @@ Three layers stacked on one SQLite file (per shard): - **Providence cache** — Q&A records keyed on the v9.8 8-dimension invariant; every record carries an audit_mode and a Merkle proof. -.. figure:: diagrams/aborist-modules.svg - :alt: Aborist module graph +.. figure:: diagrams/arborist-modules.svg + :alt: Arborist module graph :width: 100% Top-level module graph. Substrate (merkle, document) at the bottom; @@ -41,7 +41,7 @@ canonicalization compute **bit-identical** ``document_root`` hashes. That is the v9.8 admissibility property: identity by content, not by location. -Aborist uses fox's existing Go Merkle conventions verbatim +Arborist uses fox's existing Go Merkle conventions verbatim (``proxy.unturf.com/pkg/verified/merkle.go``): - **Leaf hash:** ``sha256(0x00 || canonical_chunk_bytes)``. @@ -78,7 +78,7 @@ with ``event_hash = sha256(prev_event_hash || canonical(body))``. Linear chain per shard, verified by ``make chain-check-shards`` (any break is the loudest possible signal). -Always write via ``aborist.store.append_audit`` — never insert into +Always write via ``arborist.store.append_audit`` — never insert into ``audit_events`` directly. The trichotomy and the four-rung ladder @@ -168,7 +168,7 @@ framing, and **never write to providence_cache or extend the audit chain**. That invariant is what keeps ``audit_mode`` a binary classification rather than a soft score. -See :doc:`api/qa` (``aborist.qa.inspect``). +See :doc:`api/qa` (``arborist.qa.inspect``). Where to go next ---------------- diff --git a/docs/_source/conf.py b/docs/_source/conf.py index ae72f1e..b126582 100644 --- a/docs/_source/conf.py +++ b/docs/_source/conf.py @@ -1,15 +1,15 @@ -"""Sphinx configuration for Aborist API reference.""" +"""Sphinx configuration for Arborist API reference.""" import sys from pathlib import Path -# Add aborist package + local _ext (Sphinx extensions) to path +# Add arborist package + local _ext (Sphinx extensions) to path project_root = Path(__file__).parent.parent.parent sys.path.insert(0, str(project_root)) sys.path.insert(0, str(Path(__file__).parent / "_ext")) # Project info -project = "Aborist" +project = "Arborist" copyright = "2026, agent blackops" author = "fox/timehexon" version = "9.8" @@ -37,7 +37,7 @@ napoleon_attr_annotations = True # HTML output html_theme = "sphinx_book_theme" html_static_path = ["_static"] -html_title = "Aborist API Reference" +html_title = "Arborist API Reference" # Standard sphinx-book-theme layout: full project TOC on the left, # 'On this page' (current page sections) on the right. show_toc_level=3 diff --git a/docs/_source/cookbook.rst b/docs/_source/cookbook.rst index 1ae5d4e..72b4077 100644 --- a/docs/_source/cookbook.rst +++ b/docs/_source/cookbook.rst @@ -2,8 +2,8 @@ Cookbook ======== Recipes for common workflows beyond the quickstart. Each starts from -a working aborist install (``make bootstrap`` already run) and a -populated shards directory under ``~/.aborist/shards/``. +a working arborist install (``make bootstrap`` already run) and a +populated shards directory under ``~/.arborist/shards/``. Re-crawl a website to detect changes ------------------------------------- @@ -56,7 +56,7 @@ time to fail. See :doc:`api/qa` for the ``ProvidenceSource`` impl. Query across mixed corpora -------------------------- -Every shard under ``~/.aborist/shards/`` is queried automatically. +Every shard under ``~/.arborist/shards/`` is queried automatically. Mix Wikipedia, your Grok export, a crawled site, and your own git repos in one query — retrieval ranks across all of them. @@ -81,9 +81,9 @@ no auth). Point at any OpenAI-compatible endpoint via env: .. code-block:: sh - export ABORIST_LLM_ENDPOINT="https://your-vllm.example.com/v1" - export ABORIST_LLM_MODEL="meta-llama/Llama-3.1-70B-Instruct" - export ABORIST_LLM_API_KEY="..." # optional; many vLLM deploys are open + export ARBORIST_LLM_ENDPOINT="https://your-vllm.example.com/v1" + export ARBORIST_LLM_MODEL="meta-llama/Llama-3.1-70B-Instruct" + export ARBORIST_LLM_API_KEY="..." # optional; many vLLM deploys are open make query Q="..." The model id folds into ``model_profile_hash`` (one of the 8 cache @@ -140,4 +140,4 @@ keywords without changing what the LLM sees as the question: make query Q="What did Orwell mean by always at war?" K="1984 Oceania Eastasia" Provenance gap on this is tracked in -:doc:`api/qa` (``aborist.qa.query``). +:doc:`api/qa` (``arborist.qa.query``). diff --git a/docs/_source/index.rst b/docs/_source/index.rst index bfa31e1..5799e30 100644 --- a/docs/_source/index.rst +++ b/docs/_source/index.rst @@ -1,4 +1,4 @@ -Aborist API Reference +Arborist API Reference ===================== Generated from docstrings. Replaces the static modules.md. diff --git a/docs/_source/license.rst b/docs/_source/license.rst index d6b727e..2c00a3e 100644 --- a/docs/_source/license.rst +++ b/docs/_source/license.rst @@ -1,7 +1,7 @@ License ======= -Aborist ships under the **GNU Affero General Public License v3.0 (only)** +Arborist ships under the **GNU Affero General Public License v3.0 (only)** with a Permacomputer Preamble. Full text below, sourced from the ``LICENSE`` file at the repository root (single source of truth — this page just renders it). diff --git a/docs/_source/quickstart.rst b/docs/_source/quickstart.rst index e38f644..93b53fa 100644 --- a/docs/_source/quickstart.rst +++ b/docs/_source/quickstart.rst @@ -7,18 +7,18 @@ the same query, verify, falsify, and inspect surfaces. Install ------- -Aborist needs Python 3.10+, GNU make, ``curl``, and ``bzip2``. SQLite +Arborist needs Python 3.10+, GNU make, ``curl``, and ``bzip2``. SQLite 3.35+ ships with CPython. .. code-block:: sh - git clone https://git.unturf.com/engineering/unturf/aborist.git - cd aborist + git clone https://git.unturf.com/engineering/unturf/arborist.git + cd arborist make bootstrap # one-time: venv + dev extras ``make bootstrap`` creates ``.venv/``, installs the package in editable -mode with ``[dev,html]`` extras, and exposes ``aborist`` at -``.venv/bin/aborist``. No system-wide install. Re-running is a no-op +mode with ``[dev,html]`` extras, and exposes ``arborist`` at +``.venv/bin/arborist``. No system-wide install. Re-running is a no-op if the venv is up to date. After bootstrap, every workflow lives behind a ``make`` target. Run @@ -54,7 +54,7 @@ Path B — Crawl any live website and query it make query Q="who is Russell Ballestrini?" # cross-shard; picks up new shard automatically The crawl shard is named after the seed hostname -(``crawl_russell_ballestrini_net.db``) under ``~/.aborist/shards/``. +(``crawl_russell_ballestrini_net.db``) under ``~/.arborist/shards/``. ``FAST=1`` enables aggressive crawling for your own sites; ``MAX=N`` caps discovery; ``DEPTH=N`` bounds BFS. Robots ``Disallow`` is always honored. After ingest, ``make recrawl-check DOMAIN=...`` does a @@ -98,15 +98,15 @@ Llama-3.1-8B, 82K context, no auth). Override: .. code-block:: sh - export ABORIST_LLM_ENDPOINT="https://your-vllm.example/v1" - export ABORIST_LLM_MODEL="meta-llama/Llama-3.1-70B-Instruct" - export ABORIST_LLM_API_KEY="..." + export ARBORIST_LLM_ENDPOINT="https://your-vllm.example/v1" + export ARBORIST_LLM_MODEL="meta-llama/Llama-3.1-70B-Instruct" + export ARBORIST_LLM_API_KEY="..." Where to next ------------- * :doc:`api/makefile` — every ``make`` target with a one-line description -* :doc:`api/cli` — direct ``aborist`` CLI reference +* :doc:`api/cli` — direct ``arborist`` CLI reference * :doc:`api/qa` — Q&A pipeline internals (verifier, evidence, DAG) * :doc:`api/substrate` — Merkle tree + document primitives * :doc:`license` — full AGPL + Permacomputer Preamble diff --git a/docs/bench-maxing.md b/docs/bench-maxing.md index 45f36a0..9ca302a 100644 --- a/docs/bench-maxing.md +++ b/docs/bench-maxing.md @@ -48,7 +48,7 @@ during the JSON-mode hardening journey: whatever partial content the model produced and lands in the unverified bucket honestly. Retrying spends another inference round and may produce identical garbage. See - `_repair_truncated_json` in `aborist/qa/verify.py` for the + `_repair_truncated_json` in `arborist/qa/verify.py` for the pattern: walk once tracking string state + bracket stack, close / drop / balance at end-of-input. Conservative repairs only — never insert content, never fabricate keys. diff --git a/docs/benchmarks.md b/docs/benchmarks.md index 7986dfd..dce0940 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -1,6 +1,6 @@ # Benchmarks — orientation, harnesses, fixtures, discipline -Aborist ships two bench harnesses, four question fixtures, and a +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: @@ -41,7 +41,7 @@ output (`quantifier_intensity`, `scope_bound_hint`, 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 aborist. Logs every cycle +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 @@ -109,13 +109,13 @@ 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 ~/.aborist/shards --n 3 --concurrency 4 \ + --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 ~/.aborist/shards --n 3 --concurrency 4 \ + --shards-dir ~/.arborist/shards --n 3 --concurrency 4 \ --policy quantifier_guard_apply_caps=true \ --policy quantifier_reminder_enabled=true @@ -219,7 +219,7 @@ random-word stress cycles. Each entry has: "ts": , "iso_ts": "2026-05-04T...", "words": ["dismally", "heats", "Jamaicans"], "question": "", - "answer": "", + "answer": "", "audit_mode": "STRICT|HYBRID|UNGROUNDED", "verifier_method": "claim_lattice", "n_quotes": 3, "n_verified": 0, @@ -292,7 +292,7 @@ seven-point-program directives (D2/D3/D4/D6/D7). Substrate-level hygiene check; should be ~100% on lattice modes. **Audit-line tails** (rendered by -`aborist/cli.py:_render_warrant_tail`): +`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`, @@ -311,20 +311,20 @@ warnings worth the operator's attention. ```bash # Inspect preflight stage payload from any cache row -aborist providence --show-preflight CACHE_KEY_PREFIX +arborist providence --show-preflight CACHE_KEY_PREFIX # Single query with all preflight knobs flipped -aborist query "winners of all major sports?" \ +arborist query "winners of all major sports?" \ --apply-quantifier-caps --reject-broad --soft-preflight # Dry-run — assemble context but skip LLM -aborist query "..." --dry-run +arborist query "..." --dry-run # Force fresh inference (delete prior cache row first) -aborist query "..." --burn +arborist query "..." --burn # Show raw JSON record instead of human render -aborist query "..." --json +arborist query "..." --json ``` The `Makefile` exposes most of these via shorthand: diff --git a/docs/cti-architecture.md b/docs/cti-architecture.md index d33fe12..5ba1465 100644 --- a/docs/cti-architecture.md +++ b/docs/cti-architecture.md @@ -63,10 +63,10 @@ The animating principle: **the model proposes, the runtime structures, the verif - which falsification triggers stale a record **Today's codebase:** -- `aborist/qa/runner.py:ask` — orchestrates the full lookup → infer → verify → admissibility flow for the per-document path -- `aborist/qa/query.py:query` — same orchestration for the multi-source RAG path +- `arborist/qa/runner.py:ask` — orchestrates the full lookup → infer → verify → admissibility flow for the per-document path +- `arborist/qa/query.py:query` — same orchestration for the multi-source RAG path - `DEFAULT_POLICY` (runner) and `DEFAULT_QUERY_POLICY` (query) — the policy dict -- `aborist.qa.keys.governance_policy_hash` — folds policy into the cache_key +- `arborist.qa.keys.governance_policy_hash` — folds policy into the cache_key **Status:** Implicit. The layer exists as code but is not named. The de-novo doc proposes naming it; the codebase doesn't need a `prometheus.py` module today — the dispatch logic in `runner.py` and `query.py` IS PROMETHEUS-Σ. @@ -79,15 +79,15 @@ The animating principle: **the model proposes, the runtime structures, the verif - the boundary between hard checks (lexical, deterministic) and soft checks (heuristic, demote-only) **Today's codebase:** -- `aborist/qa/parse_claims.py:parse_pointer_claims` — the model's pointer-line output → `ParsedClaim` records (the CTI compile step) -- `aborist/qa/evidence.py:EvidenceObject` — typed evidence nodes with `evidence_id` (content-addressed) + `pointer_id` (prompt-facing) -- `aborist/qa/evidence.py:render_claim_lattice` — the renderer that interpolates source spans by offset -- `aborist/qa/verify.py:verify_claim_lattice` — the deterministic verifier (six hard checks + sidecar signals) -- `aborist/qa/verify.py:verify_claim_lattice_json` — same for the JSON variant -- `aborist/qa/quantifier.py:classify_question_quantifier` — pure 10-rung intensity classifier (#000008); feeds the preflight stage's `classifier` clause -- `aborist/qa/model_profiles.py:cap_for_intensity` — per-model claim-cap profiles (#000008); feeds the `answer_contract` clause's `claim_cap_resolved` -- `aborist/qa/quantifier_reminder.py:broad_quantifier_reminder` — broad-question reminder text generator (#000008); feeds the `prompt_contract` clause -- `aborist/qa/metacognition.py:preflight_question` — meta-cognition QuestionState detector suite (#000010): temporal, contradiction, false-premise-lite, out-of-corpus. Feeds the preflight stage's `question_state` clause. +- `arborist/qa/parse_claims.py:parse_pointer_claims` — the model's pointer-line output → `ParsedClaim` records (the CTI compile step) +- `arborist/qa/evidence.py:EvidenceObject` — typed evidence nodes with `evidence_id` (content-addressed) + `pointer_id` (prompt-facing) +- `arborist/qa/evidence.py:render_claim_lattice` — the renderer that interpolates source spans by offset +- `arborist/qa/verify.py:verify_claim_lattice` — the deterministic verifier (six hard checks + sidecar signals) +- `arborist/qa/verify.py:verify_claim_lattice_json` — same for the JSON variant +- `arborist/qa/quantifier.py:classify_question_quantifier` — pure 10-rung intensity classifier (#000008); feeds the preflight stage's `classifier` clause +- `arborist/qa/model_profiles.py:cap_for_intensity` — per-model claim-cap profiles (#000008); feeds the `answer_contract` clause's `claim_cap_resolved` +- `arborist/qa/quantifier_reminder.py:broad_quantifier_reminder` — broad-question reminder text generator (#000008); feeds the `prompt_contract` clause +- `arborist/qa/metacognition.py:preflight_question` — meta-cognition QuestionState detector suite (#000010): temporal, contradiction, false-premise-lite, out-of-corpus. Feeds the preflight stage's `question_state` clause. **Status:** Real. CTI is what `claim_lattice` already is. The architectural insight from fox's synthesis — that this is an INTERNAL bytecode for reasoning, not a model output format — is the right reading: the model emits weak pointer lines; the runtime parses them into the CTI lattice; the lattice is what the verifier and renderer operate on. The pre-answer preflight contract (#000008 quantifier classifier + #000010 metacognition QuestionState) extends CTI upstream of retrieval — the runtime now classifies the *question* before generating an answer, so policy can refuse, cap, or reject without burning an LLM call. @@ -119,12 +119,12 @@ The animating principle: **the model proposes, the runtime structures, the verif **Audit-replay payoff:** two cache rows that share the same question + same model output + same verifier verdict but different preflight policy state (cap on/off, reminder on/off, reject path taken/not, metacog gates) now produce **different `run_dag_root`** values. **Today's codebase:** -- `aborist/qa/dag.py:build_run_dag` — emits the 7/8/9/10-stage DAG; returns `{root, nodes, blob}`. `preflight_hash` parameter optional for backward-compat with legacy roots. -- `aborist/qa/dag.py:build_reject_run_dag` — emits the 3-stage reject-path DAG. -- `aborist/qa/dag.py:preflight_node_hash` + `build_preflight_node_payload` — canonical hash of the preflight clause set. -- `aborist/merkle.py` — the non-commutative HashCombine + odd-self-duplicate Merkle conventions (Python port of `proxy.unturf.com/pkg/verified/merkle.go`) -- `aborist.store.append_audit` — the only legal entry point for `audit_events`; computes `event_hash = sha256(prev_event_hash || canonical(body))` -- `aborist/qa/keys.py:cache_key` — the 8-dim composite hash that gates record reuse +- `arborist/qa/dag.py:build_run_dag` — emits the 7/8/9/10-stage DAG; returns `{root, nodes, blob}`. `preflight_hash` parameter optional for backward-compat with legacy roots. +- `arborist/qa/dag.py:build_reject_run_dag` — emits the 3-stage reject-path DAG. +- `arborist/qa/dag.py:preflight_node_hash` + `build_preflight_node_payload` — canonical hash of the preflight clause set. +- `arborist/merkle.py` — the non-commutative HashCombine + odd-self-duplicate Merkle conventions (Python port of `proxy.unturf.com/pkg/verified/merkle.go`) +- `arborist.store.append_audit` — the only legal entry point for `audit_events`; computes `event_hash = sha256(prev_event_hash || canonical(body))` +- `arborist/qa/keys.py:cache_key` — the 8-dim composite hash that gates record reuse **Status:** Real. The base 7/9-stage DAG was the F-track work earlier in the session; preflight binding landed 2026-05-03 (#000009 base, `c36e85c`) and was restructured to nested-clause shape 2026-05-04 (`111dda6`). @@ -136,9 +136,9 @@ The animating principle: **the model proposes, the runtime structures, the verif - the renderer pulls source text by `(chunk_root, offset_start, offset_end)` — the model NEVER owns the quote text; the runtime interpolates it **Today's codebase:** -- `aborist/qa/evidence.py` — `EvidenceObject(source_root, document_uri, chunk_root, offset_start, offset_end, source_role, text_hash, span, evidence_id, pointer_id)`. The `pointer_id` (E1, E2, …) is what the model sees in the prompt; the `evidence_id` (content-addressed) is the cache/run-DAG handle. The runtime maps pointer_id → object → content-addressed evidence_id internally. -- `aborist/qa/verify.py:verify_claim_lattice` step 6 (citation-overlap check) — the `claim → cited_span` lexical-coverage gate -- `aborist/qa/evidence.py:render_claim_lattice` — interpolates source spans by offset, not Hermes text. Synthetic-elision is impossible by construction in pointer mode because the model never produces the quote string. +- `arborist/qa/evidence.py` — `EvidenceObject(source_root, document_uri, chunk_root, offset_start, offset_end, source_role, text_hash, span, evidence_id, pointer_id)`. The `pointer_id` (E1, E2, …) is what the model sees in the prompt; the `evidence_id` (content-addressed) is the cache/run-DAG handle. The runtime maps pointer_id → object → content-addressed evidence_id internally. +- `arborist/qa/verify.py:verify_claim_lattice` step 6 (citation-overlap check) — the `claim → cited_span` lexical-coverage gate +- `arborist/qa/evidence.py:render_claim_lattice` — interpolates source spans by offset, not Hermes text. Synthetic-elision is impossible by construction in pointer mode because the model never produces the quote string. **Status:** Real. Pointer mode makes the reverse-RAG direction explicit; the model produces the LEFT side of the chain (claim) and points to the RIGHT side (evidence_id) — the runtime walks the rest. @@ -151,9 +151,9 @@ The animating principle: **the model proposes, the runtime structures, the verif - the deliberate honesty boundary: NOT semantic truth, NOT hallucination elimination, NOT formal proof — just admissible provenance **Today's codebase:** -- `aborist/qa/keys.py` — all eight dimensions hashed into `cache_key` -- `aborist/store.py` — schema, audit chain, transaction helpers -- `aborist/qa/runner.py` + `aborist/qa/query.py` — the cache-lookup-then-infer-then-write flow +- `arborist/qa/keys.py` — all eight dimensions hashed into `cache_key` +- `arborist/store.py` — schema, audit chain, transaction helpers +- `arborist/qa/runner.py` + `arborist/qa/query.py` — the cache-lookup-then-infer-then-write flow **Status:** Real and honest. CLAUDE.md "Schema invariants (do not break)" enforces this boundary. @@ -165,8 +165,8 @@ The animating principle: **the model proposes, the runtime structures, the verif - nothing else — every other artifact (the lattice, the verdict, the rendered prose, the run DAG) comes from the runtime **Today's codebase:** -- `aborist/qa/client.py:OpenAICompatibleClient` — HTTP client with retry on 502/503/504 -- `aborist/qa/client.py:StubClient` — offline test stub +- `arborist/qa/client.py:OpenAICompatibleClient` — HTTP client with retry on 502/503/504 +- `arborist/qa/client.py:StubClient` — offline test stub - `claim_lattice_system_prompt` + `claim_lattice_grounding_reminder` — the prompt fields that frame Hermes' role - `claim_lattice_json_system_prompt` + `claim_lattice_json_grounding_reminder` — JSON variant diff --git a/docs/diagrams/aborist-modules.dot b/docs/diagrams/aborist-modules.dot index 5dd0c41..5146bb4 100644 --- a/docs/diagrams/aborist-modules.dot +++ b/docs/diagrams/aborist-modules.dot @@ -1,12 +1,12 @@ -// Aborist module graph — top-level packages & their dependencies. +// Arborist module graph — top-level packages & their dependencies. // // Arrows point in the import direction (A → B means A imports from B). // Cluster boxes group modules with shared concerns (storage, retrieval, // federation, etc.). // -// Render: dot -Tsvg aborist-modules.dot -o aborist-modules.svg +// Render: dot -Tsvg arborist-modules.dot -o arborist-modules.svg -digraph aborist_modules { +digraph arborist_modules { rankdir=LR node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10] edge [fontname="Helvetica", fontsize=9] diff --git a/docs/diagrams/aborist-modules.svg b/docs/diagrams/aborist-modules.svg index 5a72407..b1cbf68 100644 --- a/docs/diagrams/aborist-modules.svg +++ b/docs/diagrams/aborist-modules.svg @@ -3,11 +3,11 @@ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> - + -aborist_modules +arborist_modules cluster_substrate diff --git a/docs/diagrams/mesh-data-flow.dot b/docs/diagrams/mesh-data-flow.dot index 4a104a9..9cab39a 100644 --- a/docs/diagrams/mesh-data-flow.dot +++ b/docs/diagrams/mesh-data-flow.dot @@ -1,6 +1,6 @@ -// aborist/mesh — what flows between peers. +// arborist/mesh — what flows between peers. // -// Aborist is content-addressed: two peers ingesting the same dump +// Arborist is content-addressed: two peers ingesting the same dump // compute bit-identical document_roots. Gossip exchanges identifiers, // derivations, falsifications, and witness signatures. Bulk content // only flows on cache miss, by Merkle proof of inclusion. diff --git a/docs/diagrams/mesh-epoch-lifecycle.dot b/docs/diagrams/mesh-epoch-lifecycle.dot index e60a581..6c55d05 100644 --- a/docs/diagrams/mesh-epoch-lifecycle.dot +++ b/docs/diagrams/mesh-epoch-lifecycle.dot @@ -1,4 +1,4 @@ -// aborist/mesh — epoch state machine. +// arborist/mesh — epoch state machine. // // Every roster mutation bumps a counter. Old epochs persist forever // (signatures stay verifiable). New gossip is encrypted under the @@ -18,7 +18,7 @@ digraph mesh_epoch_lifecycle { eN [label="epoch N+1\nfresh secret\nfresh envelope", fillcolor="#d6ffd6"]; historic [label="every prior epoch\nstays on disk\n(roster + signatures verifiable)", fillcolor="#ffffff", shape=note]; - none -> e0 [label="aborist mesh init --group "]; + none -> e0 [label="arborist mesh init --group "]; e0 -> e1 [label="add | kick | rotate"]; e1 -> eN [label="add | kick | rotate"]; eN -> eN [label="rotate (no roster change)"]; @@ -31,8 +31,8 @@ digraph mesh_epoch_lifecycle { style="rounded,dashed"; color="#666666"; node [fillcolor="#e8f0ff", shape=note]; - op_add [label="aborist mesh add\n--member-id bob ...\nadmin only"]; - op_kick [label="aborist mesh kick\n--member-id bob ...\nadmin only"]; - op_rotate [label="aborist mesh rotate\nany current member"]; + op_add [label="arborist mesh add\n--member-id bob ...\nadmin only"]; + op_kick [label="arborist mesh kick\n--member-id bob ...\nadmin only"]; + op_rotate [label="arborist mesh rotate\nany current member"]; } } diff --git a/docs/diagrams/mesh-epoch-lifecycle.svg b/docs/diagrams/mesh-epoch-lifecycle.svg index b3eb620..0feac21 100644 --- a/docs/diagrams/mesh-epoch-lifecycle.svg +++ b/docs/diagrams/mesh-epoch-lifecycle.svg @@ -34,7 +34,7 @@ none->e0 -aborist mesh init --group <name> +arborist mesh init --group <name> @@ -99,7 +99,7 @@ -aborist mesh add +arborist mesh add --member-id bob ... admin only @@ -109,7 +109,7 @@ -aborist mesh kick +arborist mesh kick --member-id bob ... admin only @@ -119,7 +119,7 @@ -aborist mesh rotate +arborist mesh rotate any current member diff --git a/docs/diagrams/mesh-group-decisions.dot b/docs/diagrams/mesh-group-decisions.dot index c90e51d..6c09425 100644 --- a/docs/diagrams/mesh-group-decisions.dot +++ b/docs/diagrams/mesh-group-decisions.dot @@ -1,4 +1,4 @@ -// aborist/mesh — group operator decision tree. +// arborist/mesh — group operator decision tree. // // When a group steward (admin) faces a roster question, this diagram // shows which CLI verb solves it and what guarantee comes out the @@ -19,10 +19,10 @@ digraph mesh_group_decisions { q_who [label="add, remove,\nor refresh?", shape=diamond, style="filled", fillcolor="#fff7d6"]; q_admin [label="are you\nadmin in\ncurrent epoch?", shape=diamond, style="filled", fillcolor="#fff7d6"]; - a_init [label="aborist mesh init --group \nthen mesh enable\n(creates epoch 0; founder = sole admin)", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; - a_add [label="aborist mesh add\n--member-id \n--sign-pub \n--dh-pub \n[--role admin]", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; - a_kick [label="aborist mesh kick\n--member-id \n--reason '...'", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; - a_rotate [label="aborist mesh rotate\n--reason '...'\n(any current member)", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; + a_init [label="arborist mesh init --group \nthen mesh enable\n(creates epoch 0; founder = sole admin)", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; + a_add [label="arborist mesh add\n--member-id \n--sign-pub \n--dh-pub \n[--role admin]", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; + a_kick [label="arborist mesh kick\n--member-id \n--reason '...'", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; + a_rotate [label="arborist mesh rotate\n--reason '...'\n(any current member)", shape=box, style="rounded,filled", fillcolor="#d6ffd6"]; g_admin [label="permission denied\n(only admins of the\ncurrent epoch may add or kick)", shape=box, style="rounded,filled", fillcolor="#ffe0e0"]; diff --git a/docs/diagrams/mesh-group-decisions.svg b/docs/diagrams/mesh-group-decisions.svg index 7a425a9..ad6474a 100644 --- a/docs/diagrams/mesh-group-decisions.svg +++ b/docs/diagrams/mesh-group-decisions.svg @@ -48,7 +48,7 @@ a_init -aborist mesh init --group <name> +arborist mesh init --group <name> then mesh enable (creates epoch 0; founder = sole admin) @@ -77,7 +77,7 @@ a_rotate -aborist mesh rotate +arborist mesh rotate --reason '...' (any current member) @@ -114,7 +114,7 @@ a_add -aborist mesh add +arborist mesh add --member-id <id> --sign-pub <hex> --dh-pub <hex> @@ -131,7 +131,7 @@ a_kick -aborist mesh kick +arborist mesh kick --member-id <id> --reason '...' diff --git a/docs/diagrams/mesh-identity-stack.dot b/docs/diagrams/mesh-identity-stack.dot index e0b5d69..846879d 100644 --- a/docs/diagrams/mesh-identity-stack.dot +++ b/docs/diagrams/mesh-identity-stack.dot @@ -1,4 +1,4 @@ -// aborist/mesh — cryptographic identity stack per peer. +// arborist/mesh — cryptographic identity stack per peer. // // Each peer carries two keypairs and one shared epoch secret. // Keys are raw 32-byte forms; everything lives in the standard SQLite db. diff --git a/docs/diagrams/mesh-secret-envelope.dot b/docs/diagrams/mesh-secret-envelope.dot index 1e8aadd..ff3180b 100644 --- a/docs/diagrams/mesh-secret-envelope.dot +++ b/docs/diagrams/mesh-secret-envelope.dot @@ -1,4 +1,4 @@ -// aborist/mesh — per-member secret envelope. +// arborist/mesh — per-member secret envelope. // // Every epoch carries one fresh 32-byte symmetric secret. That single // secret is wrapped N times, once per member, using ECDH between the diff --git a/docs/mesh.md b/docs/mesh.md index 4ceb1ee..c3335d9 100644 --- a/docs/mesh.md +++ b/docs/mesh.md @@ -1,7 +1,7 @@ -# Mesh — multiplayer aborist +# Mesh — multiplayer arborist -Aborist on a single laptop is a content-addressed forest of documents. -Aborist across many laptops is a *gossip-able* forest. Two peers that +Arborist on a single laptop is a content-addressed forest of documents. +Arborist across many laptops is a *gossip-able* forest. Two peers that ingest the same Wikipedia dump compute bit-identical `document_root`s, so any peer can verify another peer's claim by re-deriving the hash locally. Mesh is wire-and-trust scaffolding wrapped around that fact. @@ -20,10 +20,10 @@ Lookups, ingests, distillations, and Q&A run identically with or without mesh. Initialization is two commands and is reversible. ``` -aborist mesh init --group myteam # mint Ed25519 + X25519 keys; create epoch 0 -aborist mesh enable # flip gating flag on -aborist mesh status # always-safe inspection -aborist mesh disable # flag back off; keys + history stay on disk +arborist mesh init --group myteam # mint Ed25519 + X25519 keys; create epoch 0 +arborist mesh enable # flip gating flag on +arborist mesh status # always-safe inspection +arborist mesh disable # flag back off; keys + history stay on disk ``` ## Identity stack @@ -71,14 +71,14 @@ between their own X25519 priv and a rotator's X25519 pub, then unwraps their own slot. An evicted peer simply has no slot, so unwrap raises `ValueError` — they are opaque to gossip from epoch+1 onward by design. -Code: `aborist/mesh/state.py::_wrap_secret_for_members` and +Code: `arborist/mesh/state.py::_wrap_secret_for_members` and `unwrap_secret_for_self`. ## What flows between peers ![data flow](diagrams/mesh-data-flow.png) -Aborist is content-addressed, so identifiers are short and bodies are +Arborist is content-addressed, so identifiers are short and bodies are optional. A typical sync round looks like: 1. **alice** signs and sends `ANNOUNCE_ROOT(document_root, source_uri, @@ -120,10 +120,10 @@ is rejected; an admin who wants out runs `mesh disable` instead. ``` # bob mints his keys and shares pubs (out-of-band: signal, in person, signed file). -aborist mesh init --group myteam --member-id bob # bob's machine +arborist mesh init --group myteam --member-id bob # bob's machine # alice (admin) enrolls bob at her machine: -aborist mesh add --member-id bob \ +arborist mesh add --member-id bob \ --sign-pub \ --dh-pub # epoch bumps. fresh secret wrapped to alice + bob. @@ -135,7 +135,7 @@ design, joining doesn't grant retroactive access. ### Kick a member (admin only) ``` -aborist mesh kick --member-id dave --reason "left team 2026-04-28" +arborist mesh kick --member-id dave --reason "left team 2026-04-28" ``` Epoch bumps. New secret wrapped to everyone *except* dave. Dave's prior @@ -148,7 +148,7 @@ Cannot leave a roster with no admins. Promote first, then kick. ### Rotate the secret (any member) ``` -aborist mesh rotate --reason "scheduled monthly hygiene" +arborist mesh rotate --reason "scheduled monthly hygiene" ``` Same roster, fresh secret. Use on suspected secret leak when no @@ -203,19 +203,19 @@ Mesh does not defend against: ## Related code -- `aborist/mesh/__init__.py` — module entrypoint, public API -- `aborist/mesh/crypto.py` — Ed25519 + X25519 + ChaCha20-Poly1305 wrappers -- `aborist/mesh/state.py` — DB writes + audit chain hooks; epoch rotation -- `aborist/mesh/members.py` — `add_member`, `kick_member`, `scheduled_rotate` -- `aborist/cli.py` — `_cmd_mesh_*` argparse handlers -- `aborist/store.py` — `mesh_identity`, `mesh_roster`, `mesh_epochs` schema +- `arborist/mesh/__init__.py` — module entrypoint, public API +- `arborist/mesh/crypto.py` — Ed25519 + X25519 + ChaCha20-Poly1305 wrappers +- `arborist/mesh/state.py` — DB writes + audit chain hooks; epoch rotation +- `arborist/mesh/members.py` — `add_member`, `kick_member`, `scheduled_rotate` +- `arborist/cli.py` — `_cmd_mesh_*` argparse handlers +- `arborist/store.py` — `mesh_identity`, `mesh_roster`, `mesh_epochs` schema ## Related docs - `README.md` — top-level overview, ingest flow, single-peer Q&A - `CLAUDE.md` — schema invariants, conventions, audit chain rules - `~/git/proxy.unturf.com/pkg/verified/merkle.go` — fox's Go merkle - reference. Aborist's Python port mirrors the conventions. + reference. Arborist's Python port mirrors the conventions. ## Rendering the diagrams diff --git a/docs/qa-modes-bench.md b/docs/qa-modes-bench.md index 0937ae6..dc75532 100644 --- a/docs/qa-modes-bench.md +++ b/docs/qa-modes-bench.md @@ -2,7 +2,7 @@ **Date:** 2026-05-02 **Endpoint:** `https://hermes.ai.unturf.com/v1` (Hermes-3-Llama-3.1-8B-FP8-Dynamic, vLLM, 82K ctx) -**Corpus:** Wikipedia 2003-05-16 cur snapshot, sharded under `~/.aborist/shards` +**Corpus:** Wikipedia 2003-05-16 cur snapshot, sharded under `~/.arborist/shards` Two sweeps landed today, each 71 questions × 3 modes: @@ -28,7 +28,7 @@ Substrate-side (between `2026-05-02T11:31Z` and `2026-05-02T15:07Z`): - **Sprint 1b** — per-mode `max_context_chars`. Bench's recommended-context-budget table flows back into `DEFAULT_QUERY_POLICY["max_context_chars_by_mode"]`: quote 24 KB, pointer 24 KB, JSON 48 KB. Folds into `governance_policy_hash`. - **Sprint 2** — pointer Rule 9: chunk-specificity instruction added to the lattice-pointer system prompt. -- **DRY collapse** — the four lattice prompts (system + grounding × pointer + JSON) lifted to `aborist/qa/prompts.py` as a single source of truth, imported by both `runner.DEFAULT_POLICY` and `query.DEFAULT_QUERY_POLICY`. +- **DRY collapse** — the four lattice prompts (system + grounding × pointer + JSON) lifted to `arborist/qa/prompts.py` as a single source of truth, imported by both `runner.DEFAULT_POLICY` and `query.DEFAULT_QUERY_POLICY`. - **httpx persistent client** — the chat-completion path used to construct a fresh `httpx.Client` per call, paying a TLS handshake every request. Move to `__init__`; HTTP/1.1 keep-alive across calls. Save 1-2 min on a 426-call bench. - **Sample-level shuffled bench scheduling** — every `(question, mode, sample_idx)` is a task, shuffled with `--seed`, dispatched concurrently. Per-cell `Lock` dict serializes burn-then-write on the shared cache_key. True i.i.d. n=3 variance; vLLM batcher fed a diverse request stream. - **Bench `--resume`** — read existing JSONL, skip done tasks, append fresh rows. Stop/start-able. @@ -152,7 +152,7 @@ vLLM saturates at c=3-4 on this endpoint. More concurrent requests fill the batc Across both benches: **2 errors at 11:31Z, 6 errors at 15:07Z** — all on the same question (`tell me about the roman empire`). Lone UTF-16 surrogates in Wikipedia chunk content, two distinct paths: -- v1 (`3b91223`) — hardened SHA-256 hashers on the OUTPUT side (`aborist/qa/dag.py`, `keys.py`, `evidence.py`, `store.py` audit chain) with `errors='surrogatepass'` so the run-DAG roots survive surrogate-bearing model output. +- v1 (`3b91223`) — hardened SHA-256 hashers on the OUTPUT side (`arborist/qa/dag.py`, `keys.py`, `evidence.py`, `store.py` audit chain) with `errors='surrogatepass'` so the run-DAG roots survive surrogate-bearing model output. - v2 (`41d1d9b`) — scrubs message content INSIDE `OpenAICompatibleClient.chat_completion` before httpx's outbound JSON encode. The corpus chunk text was the path; httpx's `.encode('utf-8')` on the request body raised before the call left the client. Verified: `tell me about the roman empire` under `claim_lattice` now classifies HYBRID 5/7 instead of erroring. Next bench will land 0 errors. diff --git a/docs/seven-point-program.md b/docs/seven-point-program.md index 1a89ba9..0ac4a7d 100644 --- a/docs/seven-point-program.md +++ b/docs/seven-point-program.md @@ -25,7 +25,7 @@ not a model self-grade. - **Status**: ✓ enforced. - **Code anchors**: - `aborist/qa/verify.py:verify_quotes`, + `arborist/qa/verify.py:verify_quotes`, `verify_claim_lattice`, `verify_claim_lattice_json` — pure functions with no chat-client parameter. - **Pinning tests**: `tests/test_verify*.py`, @@ -43,9 +43,9 @@ never types the quote string itself. - **Status**: ✓ enforced for lattice modes. - **Code anchors**: - `aborist/qa/parse_claims.py:parse_pointer_claims`, - `aborist/qa/verify.py:CLAIM_LATTICE_JSON_SCHEMA`, - `aborist/qa/runner.py` (system prompts). + `arborist/qa/parse_claims.py:parse_pointer_claims`, + `arborist/qa/verify.py:CLAIM_LATTICE_JSON_SCHEMA`, + `arborist/qa/runner.py` (system prompts). - **Pinning tests**: `tests/test_claim_lattice.py`, `tests/test_verify_json.py`. - **Bench signal**: rows with `answer_mode ∈ @@ -66,9 +66,9 @@ frame and discarding the others. quantifier classifier + #000010 metacognition QuestionState) landed 2026-05-03. Answer-side multi-frame compilation still pending. -- **Code anchors**: `aborist/qa/quantifier.py:classify_question_quantifier` - (#000008), `aborist/qa/metacognition.py:preflight_question` - (#000010), `aborist/qa/dag.py:preflight_node_hash` / +- **Code anchors**: `arborist/qa/quantifier.py:classify_question_quantifier` + (#000008), `arborist/qa/metacognition.py:preflight_question` + (#000010), `arborist/qa/dag.py:preflight_node_hash` / `build_preflight_node_payload` (#000009 — five nested CTI clauses bind into the run-DAG `preflight` stage). Future: runtime-side polarity contract emission for multi-frame answers. @@ -96,17 +96,17 @@ the **preflight contract** governing the run (#000009 — guard / cap / reminder / reject decisions hash-bound). - **Status**: ¾ — `evidence_map_root` lives in the run-DAG - (`aborist/qa/dag.py`); `retrieval_plan_hash` landed via #000001. + (`arborist/qa/dag.py`); `retrieval_plan_hash` landed via #000001. Preflight stage (#000009) added the upstream control commitment: classifier output + answer/prompt/evidence contracts + `governance_policy_hash` / `model_profile_hash` references all fold into a single `preflight` Merkle leaf. Quote mode now 7→8 stages; pointer-mode CTI 9→10 stages; reject-broad path emits a 3-stage minimal DAG (`question → preflight → final_label`). -- **Code anchors**: `aborist/qa/dag.py:build_run_dag` (legacy + +- **Code anchors**: `arborist/qa/dag.py:build_run_dag` (legacy + preflight-extended shapes), `build_reject_run_dag` (3-stage), `preflight_node_hash` / `build_preflight_node_payload` (5-clause - payload), `aborist/qa/evidence.py:evidence_map_root`. + payload), `arborist/qa/evidence.py:evidence_map_root`. - **Pinning tests**: `tests/test_dag.py` (24 tests including preflight stage + reject path), `tests/test_evidence.py`. - **Open ticket**: [#000001 Retrieval-keywords audit gap](ticket-000001-retrieval-keywords-audit-gap.md) @@ -125,14 +125,14 @@ citation coverage, pointer-count cap, lexical anchor warrant). No NLI, no embeddings, no model self-grading. - **Status**: ✓ enforced. -- **Code anchors**: `aborist/qa/verify.py:verify_claim_lattice` — +- **Code anchors**: `arborist/qa/verify.py:verify_claim_lattice` — seven hard checks documented inline. - **Pinning tests**: `tests/test_claim_lattice.py` (per-rule coverage), `tests/test_verify_json.py`, `tests/test_verify.py`. - **Bench signal**: every row carries `verifier_method ∈ {quote, span, entity, paraphrase, claim_lattice, none}` (CHECK constraint - in `aborist/store.py`). + in `arborist/store.py`). ### D6 — General anchor-class warrant before semantic NLI @@ -147,8 +147,8 @@ exists in the proof path. - **Status**: ½ — relation-shape only. Entity-list / why-cause / when-date shapes pass through unchecked. - **Code anchors**: - `aborist/qa/verify.py:_extract_anchor_candidates`, - `aborist/qa/verify.py:_question_is_relation_shape`. + `arborist/qa/verify.py:_extract_anchor_candidates`, + `arborist/qa/verify.py:_question_is_relation_shape`. - **Pinning tests**: `tests/test_claim_lattice.py` covers relation-shape; entity-list / cause / date shapes have no warrant test today. @@ -175,7 +175,7 @@ stays `STRICT`/`HYBRID`/`UNGROUNDED` so v9.8 cache_key invariants hold. - **Status**: ✓ enforced for lattice modes. -- **Code anchors**: `aborist/cli.py:_render_audit_label`. +- **Code anchors**: `arborist/cli.py:_render_audit_label`. - **Pinning tests**: `tests/test_cli_render.py` — `test_render_label_strict_in_claim_lattice_becomes_evidence_linked` and siblings. @@ -253,7 +253,7 @@ passed at the original snapshot. Three additional tickets landed extending D1, D3, and D4: - **#000008** (closed `4f2b5a6`, 2026-05-03) — Broad-quantifier - preflight guard. New modules `aborist/qa/quantifier.py`, + preflight guard. New modules `arborist/qa/quantifier.py`, `model_profiles.py`, `quantifier_reminder.py`. Seven new policy fields fold into `governance_policy_hash`; six-level disable hierarchy. Defaults flipped (after n=5 verification): @@ -261,7 +261,7 @@ Three additional tickets landed extending D1, D3, and D4: opt-in with JSON-only allowlist. - **#000010** (closed, 2026-05-03) — Meta-Cognition Preflight - Guard. New module `aborist/qa/metacognition.py` with + Guard. New module `arborist/qa/metacognition.py` with `QuestionState` dataclass + four pure detectors (temporal, contradiction, false-premise-lite, out-of-corpus). Six new policy fields, all default-on except diff --git a/docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md b/docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md index 1c041d8..940c31c 100644 --- a/docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md +++ b/docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md @@ -62,7 +62,7 @@ aren't even queryable to know which records to re-run. ### 1.2 What gets logged today -The `run_dag` retrieval stage at `aborist/qa/dag.py:155` hashes only the +The `run_dag` retrieval stage at `arborist/qa/dag.py:155` hashes only the `sources_summary` (output): ```python @@ -164,27 +164,27 @@ question." The 8-dim `cache_key` invariant stays intact. ## 3. Implementation sketch (when scheduled) -1. **`aborist/qa/dag.py`** — `build_run_dag` gains `retrieval_inputs` +1. **`arborist/qa/dag.py`** — `build_run_dag` gains `retrieval_inputs` parameter; embeds it into the retrieval-stage hash. Backward-compat: when `retrieval_inputs` is None, fall back to the current sources-summary-only hash. -2. **`aborist/qa/query.py`** — at the existing run_dag construction +2. **`arborist/qa/query.py`** — at the existing run_dag construction site, pass `{"question": ..., "retrieval_keywords": ..., "top_k": ..., "over_fetch": ..., "max_context_chars": ...}` as `retrieval_inputs`. -3. **`aborist/store.py`** — schema migration: add +3. **`arborist/store.py`** — schema migration: add `retrieval_keywords TEXT` (nullable) to `providence_cache`. Mirror the existing `_rebuild_providence_cache_*` pattern only if a CHECK constraint or column-default constraint requires it (otherwise a plain `ALTER TABLE ... ADD COLUMN` suffices). -4. **`aborist/qa/query.py`** persist site — include `retrieval_keywords` +4. **`arborist/qa/query.py`** persist site — include `retrieval_keywords` in the INSERT. -5. **`aborist/qa/verify.py`** + relevant CLI render — surface keywords - in `aborist providence` output and `aborist inspect` so an operator +5. **`arborist/qa/verify.py`** + relevant CLI render — surface keywords + in `arborist providence` output and `arborist inspect` so an operator can see at a glance whether a record was retrieval-augmented. 6. **Tests:** @@ -233,16 +233,16 @@ question." The 8-dim `cache_key` invariant stays intact. **Closed 2026-05-02.** Run-DAG binding scope landed: -- New module `aborist/qa/retrieval_plan.py` — `RetrievalPlan` +- New module `arborist/qa/retrieval_plan.py` — `RetrievalPlan` dataclass + `retrieval_plan_hash` function. Captures `retrieval_keywords`, `top_k`, `over_fetch`, `max_context_chars`, `shard_ids`. SHA-256 over canonical-JSON; deterministic per call. -- `aborist/qa/dag.py:build_run_dag` accepts `retrieval_plan_hash` +- `arborist/qa/dag.py:build_run_dag` accepts `retrieval_plan_hash` parameter. When provided, the retrieval stage hash binds BOTH the plan (input) and `sources_summary` (output). When omitted, falls back to the historical sources-summary-only hash so pre-#000001 records keep their `run_dag_root` values stable. -- `aborist/qa/query.py` constructs the plan per call from the +- `arborist/qa/query.py` constructs the plan per call from the query-time inputs (keywords from `--retrieval-keywords`, `top_k` / `over_fetch` / `max_context_chars` from policy + args) plus the resolved shard set, computes the hash, and @@ -791,7 +791,7 @@ J — Retrieval Plan Provenance Binding ### A.10 Concrete diff -#### `aborist/qa/retrieval.py` +#### `arborist/qa/retrieval.py` Return a structured object: @@ -822,14 +822,14 @@ class RetrievalPlan: } ``` -#### `aborist/qa/hash.py` +#### `arborist/qa/hash.py` ```python def retrieval_plan_hash(plan: RetrievalPlan) -> str: - return hash_json("aborist-retrieval-plan-v1", plan.canonical()) + return hash_json("arborist-retrieval-plan-v1", plan.canonical()) ``` -#### `aborist/qa/dag.py` +#### `arborist/qa/dag.py` Change retrieval node from: diff --git a/docs/tickets/ticket-000002-reference-frame-polarity-contract.md b/docs/tickets/ticket-000002-reference-frame-polarity-contract.md index c14d592..691828e 100644 --- a/docs/tickets/ticket-000002-reference-frame-polarity-contract.md +++ b/docs/tickets/ticket-000002-reference-frame-polarity-contract.md @@ -120,7 +120,7 @@ The additive insight: Three new pieces, each scoped tight: -### 4.1 Frame detector (`aborist/qa/frame.py`) +### 4.1 Frame detector (`arborist/qa/frame.py`) Given a question + retrieved sources, detect the reference frame: @@ -242,7 +242,7 @@ To add when this ticket lands: **Closed 2026-05-02.** Landed via: -- New module `aborist/qa/frame.py` — `FrameDetection` dataclass +- New module `arborist/qa/frame.py` — `FrameDetection` dataclass + `detect_frame(question, sources, phrase_match_roots)`. Detects reference-frame queries via the conjunction of (phrase route fired) AND (phrase-matched source is a reference work — title @@ -251,7 +251,7 @@ To add when this ticket lands: `protagonist` / `plot`). Distinct-marker count (not total) keeps the heuristic robust against single-marker repetition (e.g. a history article saying "novel approach" twice doesn't trip). -- `aborist/qa/query.py` calls `detect_frame` for lattice modes, +- `arborist/qa/query.py` calls `detect_frame` for lattice modes, using the article LEAD (chunk_idx=0, post-wikitext-strip) as the body sample so fiction markers cluster where they appear on Wikipedia (lead paragraph). diff --git a/docs/tickets/ticket-000003-anchor-class-warrant.md b/docs/tickets/ticket-000003-anchor-class-warrant.md index 313f0e2..e6b3e02 100644 --- a/docs/tickets/ticket-000003-anchor-class-warrant.md +++ b/docs/tickets/ticket-000003-anchor-class-warrant.md @@ -20,7 +20,7 @@ covers that path separately). ## 1. Problem statement -Today's warrant-lite (`aborist/qa/warrant.py:warrant_check`) covers +Today's warrant-lite (`arborist/qa/warrant.py:warrant_check`) covers two anchor classes: - **Proper-noun anchors** (relation-shape questions): for @@ -128,7 +128,7 @@ shape to constrain it. ### 3.1 Question-shape detector extension -`aborist/qa/warrant.py` already has `_question_is_relation_shape`. +`arborist/qa/warrant.py` already has `_question_is_relation_shape`. Add: ```python @@ -251,7 +251,7 @@ To add when this ticket lands: ## 6. Status **Closed 2026-05-02.** Landed via per-shape detectors + extractors -in `aborist/qa/warrant.py`: +in `arborist/qa/warrant.py`: - `_question_is_entity_list_shape` / `_question_is_count_shape` / `_question_is_why_shape` regex detectors. diff --git a/docs/tickets/ticket-000005-label-ladder-migration.md b/docs/tickets/ticket-000005-label-ladder-migration.md index 8dfa4f7..01ce587 100644 --- a/docs/tickets/ticket-000005-label-ladder-migration.md +++ b/docs/tickets/ticket-000005-label-ladder-migration.md @@ -110,13 +110,13 @@ ladder migration render under the new ladder identically. ## 5. Implementation sketch -1. **Verifier enhancement** (`aborist/qa/verify.py`): both +1. **Verifier enhancement** (`arborist/qa/verify.py`): both `verify_claim_lattice` and `verify_claim_lattice_json` add a `verifier_steps_ran: list[str]` field on the verdict dict listing which verifier paths fired (`pointer_resolve`, `source_role`, `coverage`, `pointer_cap`, `warrant`, `lazy_anchor_demote`). -2. **Renderer mapping** (`aborist/cli.py:_render_audit_label`): +2. **Renderer mapping** (`arborist/cli.py:_render_audit_label`): add the four-rung mapping function gated on `verifier_method.startswith("claim_lattice")`. 3. **Bench update** (`bench/qa_sweep.py`): per-row @@ -159,7 +159,7 @@ ladder migration render under the new ladder identically. **Closed 2026-05-02.** Landed via: - `_render_audit_label(audit_mode, verifier_method, violations)` - in `aborist/cli.py` — three-arg signature; `violations` defaults + in `arborist/cli.py` — three-arg signature; `violations` defaults to `None` for backward-compat with callers that don't have the list yet. - `_ladder_rung_for_lattice` helper computes the rung from diff --git a/docs/tickets/ticket-000006-bench-emergent-findings.md b/docs/tickets/ticket-000006-bench-emergent-findings.md index 1d735a1..2e5f64e 100644 --- a/docs/tickets/ticket-000006-bench-emergent-findings.md +++ b/docs/tickets/ticket-000006-bench-emergent-findings.md @@ -20,7 +20,7 @@ discipline (POINTER-LINKED → ANCHOR-WARRANTED → EVIDENCE-WARRANTED 1. Three random words from `/usr/share/dict/words` (`^[a-z]{5,12}$`). 2. Hermes @ temp=0.8 wove a creative question paragraph. -3. Aborist answered via `claim_lattice` mode (full pipeline). +3. Arborist answered via `claim_lattice` mode (full pipeline). 4. Append journey to `bench/emergent_log.jsonl`. Aggregate distribution: @@ -297,7 +297,7 @@ language-discourse vocabulary. The model wove a definition-style answer using overlapping vocabulary that the verifier's per-claim citation-coverage check passed. -`aborist inspect --cache-key 08dbd2c1...` confirms STRICT 1/1 +`arborist inspect --cache-key 08dbd2c1...` confirms STRICT 1/1 verified, no unverified spans, no violations. The verifier genuinely accepted the claim. @@ -347,7 +347,7 @@ that the lexical Rule-5 check cannot catch alone. 1. **Rule 9 — SUBJECT_TOKENS_ABSENT detector — landed inline (no separate ticket).** New per-claim check in - `aborist/qa/verify.py`. For each resolving claim, compute the + `arborist/qa/verify.py`. For each resolving claim, compute the set of content tokens shared by question AND claim (`_parroted_subject_tokens_absent`). For each, check substring presence in the union of cited evidence spans (lower-cased, diff --git a/docs/tickets/ticket-000007-query-layer-hyphen-fold.md b/docs/tickets/ticket-000007-query-layer-hyphen-fold.md index f1209bc..5131e12 100644 --- a/docs/tickets/ticket-000007-query-layer-hyphen-fold.md +++ b/docs/tickets/ticket-000007-query-layer-hyphen-fold.md @@ -98,7 +98,7 @@ breadth metric for non-hyphen queries. - Zero blast radius for non-hyphen queries. **Cons:** -- Body-FTS still uses `aborist/search/fts5.py:_query_tokens` (a +- Body-FTS still uses `arborist/search/fts5.py:_query_tokens` (a separate function) and stays on AND-mode `bi AND polar AND rare`. Body-FTS won't pull in `Bipolar disorder` directly. Title-route + accept-path 5 + rerank carries the recall load. Acceptable: title @@ -140,7 +140,7 @@ bench shows it's worth the version-bump cost. ## 3. Implementation sketch -1. **`aborist/qa/query.py`** — new module-level constant +1. **`arborist/qa/query.py`** — new module-level constant `_HYPHEN_RUN_RE = re.compile(r"[A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)+")` plus pure helper: @@ -234,7 +234,7 @@ bench shows it's worth the version-bump cost. Closed 2026-05-02. Landed Option A as designed: -- `aborist/qa/query.py` — `_HYPHEN_RUN_RE` constant + +- `arborist/qa/query.py` — `_HYPHEN_RUN_RE` constant + `_hyphen_fold_variants(s)` helper. `_title_query_tokens(s)` additively merges the variants. `_filter_by_title_relevance` gains optional `hyphen_fold_anchors` kwarg + accept-path 5 @@ -242,8 +242,8 @@ Closed 2026-05-02. Landed Option A as designed: when title-breadth fails). `_rerank` threads the kwarg. `_search_corpus` caller computes `_hyphen_fold_variants(retrieval_query)` and passes through. -- `aborist/qa/query.py:DEFAULT_QUERY_POLICY` and - `aborist/qa/runner.py:DEFAULT_POLICY` — `hyphen_fold_v1: True` +- `arborist/qa/query.py:DEFAULT_QUERY_POLICY` and + `arborist/qa/runner.py:DEFAULT_POLICY` — `hyphen_fold_v1: True` marker. Folds into `governance_policy_hash` so records produced under the new rule cache-split cleanly. - `tests/test_query.py` — 4 new tests: diff --git a/docs/tickets/ticket-000008-broad-quantifier-preflight-guard.md b/docs/tickets/ticket-000008-broad-quantifier-preflight-guard.md index f89c08d..f53e66e 100644 --- a/docs/tickets/ticket-000008-broad-quantifier-preflight-guard.md +++ b/docs/tickets/ticket-000008-broad-quantifier-preflight-guard.md @@ -61,7 +61,7 @@ intensity (Section 2). Fox sketched it as: The categories below derive from formal-semantics quantifier theory (Mostowski generalized quantifiers; Barwise-Cooper; Partee D- vs A- -quantifiers) intersected with the operational axis aborist actually +quantifiers) intersected with the operational axis arborist actually needs: **expected number of claims in the answer**. Categories that don't change the expected answer length are dropped from this table and surfaced as adjacent dimensions in Section 2.1. @@ -114,7 +114,7 @@ ladder, and may warrant their own ticket later. modify a numeric bound (`about ten`) — fold into the SMALL_NUM_EXPLICIT or COMPARATIVE_BOUND rung with the bound. -### 2.2 Why these specific categories matter for aborist +### 2.2 Why these specific categories matter for arborist Each new rung names a *distinct expected-answer-length distribution*: @@ -226,7 +226,7 @@ Original (superseded): Land Option D. Concrete plan: -1. New module `aborist/qa/quantifier.py` with +1. New module `arborist/qa/quantifier.py` with `classify_question_quantifier(question: str) -> dict` returning `{"intensity": "ALL"|"MANY"|"FEW"|"SMALL_NUM"|"SINGULAR"|"ABSENT", "matched_token": str, "explicit_count": int | None}`. Pure @@ -240,9 +240,9 @@ Land Option D. Concrete plan: across the board, so opting in costs nothing). 3. Per-model profile registry. `model_profile_hash` (already in the 8-dim cache key) gets a quantifier-cap profile attached. The - profile lives in `aborist/qa/model_profiles.py` (new file). -4. Preflight wiring in `aborist/qa/runner.py:ask` and - `aborist/qa/query.py:query`: classify the question, look up the + profile lives in `arborist/qa/model_profiles.py` (new file). +4. Preflight wiring in `arborist/qa/runner.py:ask` and + `arborist/qa/query.py:query`: classify the question, look up the intensity-keyed cap from the model profile, override the `claim_lattice_max_claims_per_answer` for this call. 5. Optional reminder injection in the prompt builder (gated on @@ -366,7 +366,7 @@ Originally tracked here as a gap — closed in this same session. Landed: -- `aborist/qa/query.py:2580-2592` — `format_collapsed` and +- `arborist/qa/query.py:2580-2592` — `format_collapsed` and `raw_answer` surfaced on the `query()` result dict. - `bench/qa_sweep.py:_run_one` — three new fields per row: `format_collapsed`, `violation_kinds` (sorted unique kind @@ -428,7 +428,7 @@ enhancement, not blocking. MANY? The "tell me all there is to know" precedent (york-england case, ticket #000006) suggests yes — operationally it produces the same enumeration pressure. -- Does the per-model profile belong in `aborist/qa/model_profiles.py` +- Does the per-model profile belong in `arborist/qa/model_profiles.py` or extend the existing `model_profile_hash` derivation in `keys.py`? Initial answer: new file, hash-derived from the profile dict so changing a cap invalidates prior records. @@ -441,7 +441,7 @@ enhancement, not blocking. surfaces through the existing TOO_MANY_CLAIMS violation path on the audit-line tail. - This ticket does NOT touch retrieval. Quantifier guard runs at - policy layer only; retrieval pipeline (`aborist/qa/query.py` + policy layer only; retrieval pipeline (`arborist/qa/query.py` Sections 1–9) stays untouched. ## 8. Status @@ -476,9 +476,9 @@ Implementation commit chain (all on `origin/main`): |-----------|-------|----------------------------------------------------------|-------| | `2ffed00` | 0 | bench-harness extension (FC rate, violation kinds) | +5 | | `5e8d662` | 0.x | bench-row telemetry (pointer/bracket/profile fields) | +5 | -| `926b05e` | 1 | pure quantifier classifier `aborist/qa/quantifier.py` | +61 | -| `84d5b5c` | 2 | model-profile caps `aborist/qa/model_profiles.py` | +19 | -| `6f90f21` | 3 | reminder injection `aborist/qa/quantifier_reminder.py` | +19 | +| `926b05e` | 1 | pure quantifier classifier `arborist/qa/quantifier.py` | +61 | +| `84d5b5c` | 2 | model-profile caps `arborist/qa/model_profiles.py` | +19 | +| `6f90f21` | 3 | reminder injection `arborist/qa/quantifier_reminder.py` | +19 | | `5a60e85` | 4 | CLI flags + 4 violation kinds + reject-broad early-return | +16 | Total: **906 tests passing (120 new); 36 skipped.** @@ -503,7 +503,7 @@ quantifier_guard_modes ["claim_lattice_pointer", reject_broad, caps_by_intensity, guard_modes, plus Phase 0 format_collapse_check_enabled) 4. Per-mode: `quantifier_guard_modes` (quote opts out by default) -5. Per-model: `aborist/qa/model_profiles.py` PROFILES dict +5. Per-model: `arborist/qa/model_profiles.py` PROFILES dict 6. Master via governance: all 7 quantifier policy fields fold into `_VERIFIER_POLICY_FIELDS` so flipping any of them invalidates prior cache records on lookup @@ -511,7 +511,7 @@ quantifier_guard_modes ["claim_lattice_pointer", Live end-to-end verification (post-`5a60e85`): ``` -$ aborist query --reject-broad "Winners of all major sports?" +$ arborist query --reject-broad "Winners of all major sports?" Winners of all major sports? UNGROUNDED · via BROAD_QUANTIFIER_REJECTED · ALL ("all") · cap was 8 0/0 0.0s (preflight) @@ -521,7 +521,7 @@ Winners of all major sports? universe. Narrow ... or run with --allow-broad for exploratory enumeration. -$ aborist query --reject-broad "name all members of the Beatles" +$ arborist query --reject-broad "name all members of the Beatles" name all members of the Beatles UNGROUNDED · via claim_lattice · title mismatch 4/4 20.9s [Beatles enumerated; scope_bound_hint=bounded → not rejected] @@ -747,7 +747,7 @@ _RE`, `_POINTER_ID_RE`) avoid per-row recompilation. #### Phase 1 — Pure quantifier classifier (LANDED `926b05e`) -`aborist/qa/quantifier.py` with `classify_question_quantifier()`, +`arborist/qa/quantifier.py` with `classify_question_quantifier()`, pure function (no I/O, no model call, no retrieval call). Returns: ```json @@ -780,7 +780,7 @@ refinement. #### Phase 2 — Model-profile claim-budget contract (LANDED `84d5b5c`) -`aborist/qa/model_profiles.py` ships two profiles: +`arborist/qa/model_profiles.py` ships two profiles: - `adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic` — tight caps for broad intensities (ALL=8, COMPREHENSIVE=5, OPEN_REQUEST=5). @@ -821,7 +821,7 @@ the default with the looked-up cap. #### Phase 3 — Prompt reminder (LANDED `6f90f21`, default OFF) -`aborist/qa/quantifier_reminder.py` ships +`arborist/qa/quantifier_reminder.py` ships `broad_quantifier_reminder()` with two templates: - **Bounded universe** (`all members of the Beatles`): @@ -854,7 +854,7 @@ beyond the §10.8 5pp floor. #### Phase 4 — CLI flags + violation tails (LANDED `5a60e85`) -CLI flags on `aborist query`: +CLI flags on `arborist query`: ``` --no-quantifier-guard Level 2 disable: kills guard for one call @@ -1204,7 +1204,7 @@ BROAD_QUANTIFIER_SCOPE_UNBOUND scope-bound hint fired in 10.1; ``` Folds into `governance_policy_hash` via the §1 hard constraint and -into `_SOFT_DEMOTE_VIOLATION_KINDS` in `aborist/cli.py`. +into `_SOFT_DEMOTE_VIOLATION_KINDS` in `arborist/cli.py`. This keeps the schema and the four-rung ladder unchanged. Display labels carry the broad-quantifier signal via the existing tail @@ -1304,7 +1304,7 @@ answer_pointer_count: int count of [E\d+] tags in raw_answer answer_chars_with_brackets: int char count of bracket-tagged regions model_profile_id: str e.g. "hermes-3-llama-3.1-8b-fp8" (with the FP8 suffix; the - ABORIST_LLM_MODEL default uses the + ARBORIST_LLM_MODEL default uses the FP8-Dynamic variant) scope_bound_hint: str classifier output (10.1): bounded / unbounded / unknown @@ -1457,7 +1457,7 @@ From most granular to most global: Level 1 — Per-test override Tests pass policy={"quantifier_guard_enabled": False}. Same mechanism claim_lattice_warrant_check_enabled already uses - (aborist/qa/runner.py:191). One line. No environment changes. + (arborist/qa/runner.py:191). One line. No environment changes. Level 2 — Per-call CLI flag --no-quantifier-guard kills Phase 1+2 for one call @@ -1483,7 +1483,7 @@ Level 4 — Per-mode gating on baseline; its failure shape is different. Level 5 — Per-model profile - aborist/qa/model_profiles.py keys by model_profile_id. + arborist/qa/model_profiles.py keys by model_profile_id. Models without an entry fall back to the "default" profile, which has the conservative caps from §10.6 (no Hermes-tight tightening). @@ -1612,18 +1612,18 @@ Single-source-of-truth for **what was actually built** in the | Path | Purpose | Phase | |-------------------------------------|------------------------------------------------------------------------|-------| -| `aborist/qa/quantifier.py` | Pure 10-rung classifier + scope-bound heuristic | 1 | -| `aborist/qa/model_profiles.py` | PROFILES dict + `cap_for_intensity()` + `EXPLICIT_COUNT` sentinel | 2 | -| `aborist/qa/quantifier_reminder.py` | `broad_quantifier_reminder()` with bounded vs unbounded templates | 3 | +| `arborist/qa/quantifier.py` | Pure 10-rung classifier + scope-bound heuristic | 1 | +| `arborist/qa/model_profiles.py` | PROFILES dict + `cap_for_intensity()` + `EXPLICIT_COUNT` sentinel | 2 | +| `arborist/qa/quantifier_reminder.py` | `broad_quantifier_reminder()` with bounded vs unbounded templates | 3 | ### 11.2 Modified modules (new behavior) | Path | Change | Phase | |-----------------------|---------------------------------------------------------------------------------------------|-------| -| `aborist/qa/query.py` | classifier wired post-policy-resolution; `effective_max_claims` lookup; reject-broad early-return; reminder injection | 1, 2, 3, 4 | -| `aborist/qa/runner.py` | same wiring as query() for `aborist ask` parity; new policy fields in DEFAULT_POLICY | 1, 2, 3, 4 | -| `aborist/qa/keys.py` | 7 new fields in `_VERIFIER_POLICY_FIELDS` for governance-hash binding | 2, 3, 4 | -| `aborist/cli.py` | 4 new CLI flags; 4 new violation kinds in `_SOFT_DEMOTE_VIOLATION_KINDS`; `_render_warrant_tail` extended; `_render_query_human` branch for `broad_quantifier_rejected` status | 4 | +| `arborist/qa/query.py` | classifier wired post-policy-resolution; `effective_max_claims` lookup; reject-broad early-return; reminder injection | 1, 2, 3, 4 | +| `arborist/qa/runner.py` | same wiring as query() for `arborist ask` parity; new policy fields in DEFAULT_POLICY | 1, 2, 3, 4 | +| `arborist/qa/keys.py` | 7 new fields in `_VERIFIER_POLICY_FIELDS` for governance-hash binding | 2, 3, 4 | +| `arborist/cli.py` | 4 new CLI flags; 4 new violation kinds in `_SOFT_DEMOTE_VIOLATION_KINDS`; `_render_warrant_tail` extended; `_render_query_human` branch for `broad_quantifier_rejected` status | 4 | | `bench/qa_sweep.py` | `_bracket_diagnostics()`; module regexes; 8 new fields in `_run_one`; aggregation in `_summarize`; new markdown section | 0, 0.x | ### 11.3 New test files @@ -1640,7 +1640,7 @@ Total new tests: **120**; full suite **906 passing, 36 skipped.** ### 11.4 New policy fields (governance-hash bound) -All seven fold into `_VERIFIER_POLICY_FIELDS` (`aborist/qa/keys.py`) +All seven fold into `_VERIFIER_POLICY_FIELDS` (`arborist/qa/keys.py`) so flipping any of them invalidates prior cache records on lookup. ``` @@ -1653,7 +1653,7 @@ quantifier_reject_broad False Phase 4 reject default off claim_lattice_format_collapse_check_enabled True (Phase 0 prereq) ``` -### 11.5 New CLI flags (`aborist query`) +### 11.5 New CLI flags (`arborist query`) ``` --no-quantifier-guard Level 2 disable (kills guard for one call) @@ -1666,7 +1666,7 @@ claim_lattice_format_collapse_check_enabled True (Phase 0 prereq) Three soft demotes (cap ladder at ANCHOR-WARRANTED) + one hard demote (early-return UNGROUNDED). All four render audit-line tails via -`_render_warrant_tail` in `aborist/cli.py`: +`_render_warrant_tail` in `arborist/cli.py`: ``` BROAD_QUANTIFIER_RUNAWAY soft → "broad runaway" @@ -1711,33 +1711,33 @@ Choices made during implementation that didn't pre-exist in §9 / §10 but are now binding via tests + commits: 1. **`RUNG_PRIORITY` puts COMPREHENSIVE above OPEN_REQUEST** - (`aborist/qa/quantifier.py:_RUNG_PRIORITY`). Caught during Phase 1 + (`arborist/qa/quantifier.py:_RUNG_PRIORITY`). Caught during Phase 1 tests when `tell me everything about all wars` matched both templates. COMPREHENSIVE wins because §2.2 calls it "strictly stronger than ALL"; OPEN_REQUEST is a softer shape-detector. 2. **Bounded-domain anchor regex list** - (`aborist/qa/quantifier.py:_BOUNDED_DOMAIN_ANCHORS`). Hand-curated + (`arborist/qa/quantifier.py:_BOUNDED_DOMAIN_ANCHORS`). Hand-curated list covering Beatles, US states/presidents, planets, founding fathers, continents, oceans, plus a year-anchor `(?:19|20)\d{2}` pattern and a season/year/championship/tournament prefix. Future refinement (§10.1 noted): corpus-arity check. -3. **Reminder templates** (`aborist/qa/quantifier_reminder.py`). +3. **Reminder templates** (`arborist/qa/quantifier_reminder.py`). Two-template split (bounded vs unbounded); the bounded template omits the "do not enumerate from training prior" clause because the corpus has the answer set. Unknown scope falls back to the stricter unbounded template (over-warn rather than under-warn). -4. **Reject-broad answer_text format** (`aborist/qa/query.py`). +4. **Reject-broad answer_text format** (`arborist/qa/query.py`). `BROAD-QUANTIFIER PREFLIGHT REJECTED · scope unbounded` header + actionable narrowing hints (year, league, country, category) + `--allow-broad` escape-hatch mention. Format chosen so an operator reading raw output sees both the WHY and the HOW-TO-FIX. 5. **`_render_query_human` branch for `broad_quantifier_rejected` - status** (`aborist/cli.py`). Dedicated render path so the new + status** (`arborist/cli.py`). Dedicated render path so the new status doesn't fall through to the generic "unknown error" handler. Renders intensity, matched token, cap, and the rejection text on a single audit-line + body. 6. **EXPLICIT_COUNT defensive fallback to MANY cap** - (`aborist/qa/model_profiles.py:cap_for_intensity`). When the + (`arborist/qa/model_profiles.py:cap_for_intensity`). When the classifier fired SMALL_NUM_EXPLICIT or COMPARATIVE_BOUND but didn't extract a count (shouldn't happen but covers regressions), fall back to the same profile's MANY cap rather than crash. @@ -1750,10 +1750,10 @@ but are now binding via tests + commits: ### 11.10 Live verification artifacts -- `aborist query --reject-broad "Winners of all major sports?"` → +- `arborist query --reject-broad "Winners of all major sports?"` → preflight rejection, 0.0s, no LLM call. Rendered output recorded in §8 Status. -- `aborist query --reject-broad "name all members of the Beatles"` +- `arborist query --reject-broad "name all members of the Beatles"` → ran the LLM (scope_bound_hint=bounded correctly suppressed rejection), enumerated 4 verified Beatles members. Rendered output recorded in §8 Status. @@ -2239,10 +2239,10 @@ quote: Code changes (commit pending): -- `aborist/qa/runner.py`: `quantifier_reminder_enabled=True`, +- `arborist/qa/runner.py`: `quantifier_reminder_enabled=True`, new field `quantifier_apply_caps_modes=["claim_lattice"]`. -- `aborist/qa/query.py`: same defaults. -- `aborist/qa/keys.py`: `quantifier_apply_caps_modes` added to +- `arborist/qa/query.py`: same defaults. +- `arborist/qa/keys.py`: `quantifier_apply_caps_modes` added to `_VERIFIER_POLICY_FIELDS` so flipping it bumps governance hash. - Cap-application gate now reads: `if apply_caps AND mode in apply_caps_modes AND cap is not None:` @@ -2257,20 +2257,20 @@ Code changes (commit pending): ``` # Default behavior (no flag): reminder ON for lattice modes; cap # OFF (operator opt-in only). -$ aborist query "winners of all major sports?" +$ arborist query "winners of all major sports?" # Flip cap on for one call (JSON-only by default): -$ aborist query --apply-quantifier-caps "..." +$ arborist query --apply-quantifier-caps "..." # Override the allowlist to include pointer mode too (Option D): -$ aborist query --apply-quantifier-caps \ +$ arborist query --apply-quantifier-caps \ --policy quantifier_apply_caps_modes='["claim_lattice","claim_lattice_pointer"]' "..." # Disable the reminder for one call: -$ aborist query --policy quantifier_reminder_enabled=false "..." +$ arborist query --policy quantifier_reminder_enabled=false "..." # Master kill: -$ aborist query --no-quantifier-guard "..." +$ arborist query --no-quantifier-guard "..." ``` **Six-level disable hierarchy still intact** — no flag forced upon diff --git a/docs/tickets/ticket-000009-quantifier-preflight-dag-binding.md b/docs/tickets/ticket-000009-quantifier-preflight-dag-binding.md index c82506f..f4123ce 100644 --- a/docs/tickets/ticket-000009-quantifier-preflight-dag-binding.md +++ b/docs/tickets/ticket-000009-quantifier-preflight-dag-binding.md @@ -146,7 +146,7 @@ split if a future clause needs independent hashing.** ### 3.3 Where the node lives in code -- New helper in `aborist/qa/dag.py` (alongside existing per-stage +- New helper in `arborist/qa/dag.py` (alongside existing per-stage helpers): `_quantifier_preflight_node(quantifier, policy, effective_max_claims, reminder_injected)`. - Wire into `query()` at the same point quantifier classification @@ -176,7 +176,7 @@ preflight contract"). ## 4. Implementation sketch ``` -1. New function in aborist/qa/dag.py: +1. New function in arborist/qa/dag.py: def quantifier_preflight_node(quantifier, policy, ...) 2. Wire it into query() between policy resolution and t_start. 3. Wire it into runner.ask() symmetrically. @@ -219,17 +219,17 @@ This ticket closes the loop. **Closed · landed 2026-05-03 (zero-shot).** Mechanism shipped end- to-end: -- `aborist/qa/dag.py:preflight_node_hash()` — combines +- `arborist/qa/dag.py:preflight_node_hash()` — combines QuestionState + quantifier classifier output + behavioral policy_state into one canonical SHA-256 hex. -- `aborist/qa/dag.py:build_run_dag()` — new optional +- `arborist/qa/dag.py:build_run_dag()` — new optional `preflight_hash` parameter inserts a `preflight` stage at position 1 (between `question` and `retrieval`). Quote-mode shape becomes 8 stages; pointer-mode CTI shape becomes 10 stages. Backward-compatible: when `preflight_hash` is None, shapes stay 7/9 exactly so legacy `run_dag_root` values re-validate. -- `aborist/qa/query.py` + `aborist/qa/runner.py` — both build +- `arborist/qa/query.py` + `arborist/qa/runner.py` — both build the preflight payload from `question_state`, the quantifier dict, and a 10-field `policy_state` capturing the *behavioral* decisions taken on this run (guard_enabled, apply_caps_active, @@ -276,7 +276,7 @@ preflight contract"). ### 7.2 What's NOT in this ticket - **CLI flag for inspecting preflight node**: a future - `aborist providence --show-preflight ` would render + `arborist providence --show-preflight ` would render the preflight payload from `run_dag_blob`. Out of scope here. - **Bench harness preflight-hash field**: bench rows could surface `preflight_hash` (12-char prefix like `cache_key`) @@ -369,7 +369,7 @@ Confirmed. Corrections stay strictly in the audit-binding lane. **A — reject-path DAG** (the critical gap): -`aborist/qa/dag.py:build_reject_run_dag()` ships. Builds a 3-stage +`arborist/qa/dag.py:build_reject_run_dag()` ships. Builds a 3-stage `question → preflight → final_label` DAG for the reject-broad early-return path. `query()` now wires it in and returns `run_dag_root` + `run_dag_blob` on the rejection result dict. diff --git a/docs/tickets/ticket-000010-metacognition-preflight-guard.md b/docs/tickets/ticket-000010-metacognition-preflight-guard.md index 7c47207..c611182 100644 --- a/docs/tickets/ticket-000010-metacognition-preflight-guard.md +++ b/docs/tickets/ticket-000010-metacognition-preflight-guard.md @@ -158,7 +158,7 @@ follow-up; first pass surfaces it on the result dict only). ### 6.1 Quantifier (REUSE from #000008) -Already shipped as `aborist/qa/quantifier.py`. Returns intensity + +Already shipped as `arborist/qa/quantifier.py`. Returns intensity + `scope_bound_hint`. Meta-cognition wraps it; doesn't duplicate. ### 6.2 Temporal sensitivity @@ -204,7 +204,7 @@ record `false_premise_suspected` so the audit-line can surface it. ### 6.5 Reference-frame detector Reuse the existing frame-detection logic from ticket #000002 -(`aborist/qa/query.py:_detect_frame`). Already classifies +(`arborist/qa/query.py:_detect_frame`). Already classifies `reference` vs `literal` vs `ambiguous`. Meta-cognition wraps the output as `reference_frames: list[str]`. @@ -216,11 +216,11 @@ has no matching upload, sets `corpus_status: "out_of_corpus"`. Conservative — defaults to `"likely_in_corpus"` for typical encyclopedic questions. -## 7. Implementation in Aborist +## 7. Implementation in Arborist ### 7.1 New module -`aborist/qa/metacognition.py` with: +`arborist/qa/metacognition.py` with: ```python def preflight_question( @@ -325,13 +325,13 @@ to fox. ### 12.1 Phase 1 — module + detectors (LANDED `55efb04`) -`aborist/qa/metacognition.py` shipped with: +`arborist/qa/metacognition.py` shipped with: - `QuestionState` dataclass (frozen, JSON-serializable via `to_dict`) - `preflight_question()` pure function - 4 new deterministic detectors (temporal, contradiction, false- premise-lite, out-of-corpus) -- Reuses `aborist.qa.quantifier.classify_question_quantifier` +- Reuses `arborist.qa.quantifier.classify_question_quantifier` - 8 LogicalStatus values + 3 PreflightResult values - 6 per-detector enable switches in policy - `preflight_policy_hash` for governance binding (Phase 3) @@ -372,7 +372,7 @@ metacognition_block_on_contradiction False All folded into `_VERIFIER_POLICY_FIELDS` so flipping any of them invalidates prior cache records. -CLI flags on `aborist query`: +CLI flags on `arborist query`: ``` --no-preflight disable the guard for one call diff --git a/docs/tickets/ticket-000011-soft-preflight-hint-sidecar.md b/docs/tickets/ticket-000011-soft-preflight-hint-sidecar.md index 666dc66..cadd3b6 100644 --- a/docs/tickets/ticket-000011-soft-preflight-hint-sidecar.md +++ b/docs/tickets/ticket-000011-soft-preflight-hint-sidecar.md @@ -61,7 +61,7 @@ class SoftPreflightHint: The hint always carries the `SOFT_` prefix on its `classifier_label` so it can never be mistaken for a deterministic verdict by: -- The audit-line tail renderer in `aborist/cli.py:_render_warrant_tail` +- The audit-line tail renderer in `arborist/cli.py:_render_warrant_tail` - The verifier (which doesn't read sidecar fields anyway) - Bench aggregations - Operators inspecting bench JSONL or run-DAG blobs @@ -69,11 +69,11 @@ so it can never be mistaken for a deterministic verdict by: ## 3. Where it lives ``` -aborist/qa/soft_preflight.py — sidecar implementation +arborist/qa/soft_preflight.py — sidecar implementation (new module) ``` -Mirrors `aborist/qa/inspect.py` (the existing read-only sidecar +Mirrors `arborist/qa/inspect.py` (the existing read-only sidecar diagnostic for span classification) — same architectural pattern: soft signal, never enters proof path, never written to providence cache, never bumps governance hash on use. @@ -99,7 +99,7 @@ remains driven by the hard policy. ## 5. Implementation sketch ```python -# aborist/qa/soft_preflight.py +# arborist/qa/soft_preflight.py def soft_preflight_question( question: str, @@ -149,17 +149,17 @@ def soft_preflight_question( ## 6. Integration points -- `aborist/qa/query.py` — call `soft_preflight_question()` after +- `arborist/qa/query.py` — call `soft_preflight_question()` after `preflight_question()` (the hard call) but before the main LLM call. Soft hint surfaces on result dict as `soft_preflight_hint`; merges into the run-DAG `preflight` stage payload as a sub-field of `question_state`. -- `aborist/qa/runner.py` — same wiring. -- `aborist/cli.py` — new `--soft-preflight` flag (per-call +- `arborist/qa/runner.py` — same wiring. +- `arborist/cli.py` — new `--soft-preflight` flag (per-call override; default off matches policy default). - `bench/qa_sweep.py` — bench rows pick up `soft_preflight_label` + `soft_preflight_confidence` as bounded-projection fields. -- `aborist/cli.py:_render_warrant_tail` — soft hints render as +- `arborist/cli.py:_render_warrant_tail` — soft hints render as `· soft:

T

Real content here.", } } - with patch("aborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): + with patch("arborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): conn = connect(http_meta_db) try: result = ingest_crawled(conn, [url]) @@ -423,7 +423,7 @@ def test_ingest_crawled_keeps_xhtml(http_meta_db): def _seed_meta(conn, *, document_root: str, document_uri: str, etag: str | None = None): """Insert a documents row + http_meta row for recheck tests.""" - from aborist.store import transaction + from arborist.store import transaction with transaction(conn): conn.execute( @@ -451,7 +451,7 @@ def test_recrawl_check_classifies_304_as_fresh(tmp_path): conn.close() responses = {"https://x.com/p": {"head_status": 304}} - with patch("aborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): + with patch("arborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): conn = connect(db) try: result = recrawl_check(conn, limit=10) @@ -471,7 +471,7 @@ def test_recrawl_check_classifies_200_as_stale(tmp_path): conn.close() responses = {"https://x.com/q": {"head_status": 200}} - with patch("aborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): + with patch("arborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): conn = connect(db) try: result = recrawl_check(conn, limit=10) @@ -490,7 +490,7 @@ def test_recrawl_check_classifies_404_as_gone(tmp_path): conn.close() responses = {"https://x.com/r": {"head_status": 404}} - with patch("aborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): + with patch("arborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): conn = connect(db) try: result = recrawl_check(conn, limit=10) @@ -512,7 +512,7 @@ def test_recrawl_check_domain_filter(tmp_path): "https://keep.com/a": {"head_status": 304}, # drop.com would error if the filter is broken; we expect it to be skipped. } - with patch("aborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): + with patch("arborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): conn = connect(db) try: result = recrawl_check(conn, domain="keep.com", limit=10) @@ -531,7 +531,7 @@ def test_recrawl_check_updates_last_checked_at(tmp_path): conn.close() responses = {"https://x.com/s": {"head_status": 304}} - with patch("aborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): + with patch("arborist.sources.crawler.bridge.httpx.Client", _patched_httpx_get(responses)): conn = connect(db) try: recrawl_check(conn, limit=10) diff --git a/tests/crawler/test_web_fetch.py b/tests/crawler/test_web_fetch.py index 21f0db0..23186f8 100644 --- a/tests/crawler/test_web_fetch.py +++ b/tests/crawler/test_web_fetch.py @@ -6,7 +6,7 @@ Tests URL extraction, sources footer building, and web fetch helpers. import pytest from unittest.mock import Mock, AsyncMock, patch -from aborist.sources.crawler.web_fetch import ( +from arborist.sources.crawler.web_fetch import ( extract_url_from_content, is_web_fetch_request, build_sources_footer, @@ -85,8 +85,8 @@ class TestURLExtraction: @pytest.mark.skip( reason="chat-bot fetch triggers (has_web_fetch_trigger) were " - "dropped during the aborist lift — aborist has no chat surface; " - "see aborist/sources/crawler/__init__.py for rationale." + "dropped during the arborist lift — arborist has no chat surface; " + "see arborist/sources/crawler/__init__.py for rationale." ) class TestWebFetchDetection: """Test web fetch request detection.""" @@ -106,8 +106,8 @@ class TestWebFetchDetection: @pytest.mark.skip( reason="chat-bot fresh-fetch triggers (has_fresh_fetch_trigger) were " - "dropped during the aborist lift — aborist has no chat surface; " - "see aborist/sources/crawler/__init__.py for rationale." + "dropped during the arborist lift — arborist has no chat surface; " + "see arborist/sources/crawler/__init__.py for rationale." ) class TestFreshFetchDetection: """Test fresh fetch intent detection.""" @@ -300,7 +300,7 @@ class TestWebFetchIntegration: """Test successful fetch and cache operation""" # This would require mocking web_cache_manager # For now, just verify the function exists and has correct signature - from aborist.sources.crawler.web_fetch import fetch_and_cache + from arborist.sources.crawler.web_fetch import fetch_and_cache import inspect sig = inspect.signature(fetch_and_cache) assert 'url' in sig.parameters @@ -312,7 +312,7 @@ class TestWebFetchIntegration: @pytest.mark.asyncio async def test_fetch_and_cache_with_progress_callback(self): """Test fetch with progress callback""" - from aborist.sources.crawler.web_fetch import fetch_and_cache + from arborist.sources.crawler.web_fetch import fetch_and_cache # Mock web cache manager mock_manager = Mock() diff --git a/tests/test_bench_qa_sweep.py b/tests/test_bench_qa_sweep.py index c80b4d2..6a70e8f 100644 --- a/tests/test_bench_qa_sweep.py +++ b/tests/test_bench_qa_sweep.py @@ -18,7 +18,7 @@ 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 + arborist 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) diff --git a/tests/test_burn.py b/tests/test_burn.py index 49142bf..be72315 100644 --- a/tests/test_burn.py +++ b/tests/test_burn.py @@ -19,8 +19,8 @@ import time import pytest -from aborist.cli import _burn_cache_key -from aborist.store import append_audit, connect, transaction +from arborist.cli import _burn_cache_key +from arborist.store import append_audit, connect, transaction def _seed_providence_record(db_path, *, cache_key: str, question: str = "q?", audit_mode: str = "HYBRID") -> None: @@ -250,8 +250,8 @@ def test_burn_preserves_audit_chain(tmp_path): def test_burn_cli_invocation(tmp_path, capsys): - """`aborist burn` end-to-end through build_parser, including JSON output.""" - from aborist.cli import build_parser + """`arborist burn` end-to-end through build_parser, including JSON output.""" + from arborist.cli import build_parser db = tmp_path / "qa.db" KEY = "fa" * 32 @@ -279,8 +279,8 @@ def test_burn_kindergarten_burns_only_recent_rows(tmp_path, capsys): test seeds two rows, backdates one to 2h ago, and asserts the fresh one burns and the old one stays.""" import time as _time - from aborist.cli import build_parser - from aborist.store import transaction + from arborist.cli import build_parser + from arborist.store import transaction db = tmp_path / "qa.db" OLD = "ab" * 32 @@ -326,7 +326,7 @@ def test_burn_kindergarten_burns_only_recent_rows(tmp_path, capsys): def test_burn_kindergarten_dry_run_writes_nothing(tmp_path, capsys): """--dry-run reports what would burn but doesn't write or audit.""" - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "qa.db" KEY = "ef" * 32 @@ -369,7 +369,7 @@ def test_burn_kindergarten_dry_run_writes_nothing(tmp_path, capsys): def test_burn_kindergarten_zero_seconds_burns_everything(tmp_path, capsys): """0-second window = burn every live providence_cache row.""" - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "qa.db" K1 = "11" * 32 @@ -399,7 +399,7 @@ def test_burn_kindergarten_zero_seconds_burns_everything(tmp_path, capsys): def test_burn_kindergarten_respects_children_gate_without_force(tmp_path, capsys): """A row with falsifications is REFUSED unless --force. Audit chain still grows on burned rows; refused rows leave no audit entry.""" - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "qa.db" BURNABLE = "33" * 32 @@ -423,7 +423,7 @@ def test_burn_kindergarten_respects_children_gate_without_force(tmp_path, capsys def test_burn_cli_returns_non_zero_on_refused(tmp_path, capsys): """Non-zero exit when refused so scripts can detect and react.""" - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "qa.db" KEY = "fb" * 32 diff --git a/tests/test_burn_doc.py b/tests/test_burn_doc.py index 33e897a..4b58312 100644 --- a/tests/test_burn_doc.py +++ b/tests/test_burn_doc.py @@ -18,13 +18,13 @@ import json import time from typing import Iterator -from aborist.cli import _burn_core_root, _burn_document_root -from aborist.distill import FirstSentenceDistiller -from aborist.distill.runner import distill_existing -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.source import Source -from aborist.store import append_audit, connect, transaction +from arborist.cli import _burn_core_root, _burn_document_root +from arborist.distill import FirstSentenceDistiller +from arborist.distill.runner import distill_existing +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.source import Source +from arborist.store import append_audit, connect, transaction # --------------------------------------------------------------------------- @@ -56,7 +56,7 @@ def _doc(uri: str, content: str, *, edges=None) -> Document: # Long enough that the default tok-512-v1 chunker emits multiple chunks. _LONG = ( - "Aborist tends trees and forests of cross-linked information. " * 40 + "Arborist tends trees and forests of cross-linked information. " * 40 + "\n\n" + "Burn is the kindergarten leaf removal — children gate enforced. " * 40 ) @@ -460,7 +460,7 @@ def test_burn_document_preserves_audit_chain(tmp_path): def test_burn_cli_document_invocation(tmp_path, capsys): - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "burn.db" root = _ingest_one(db, "html://cli-doc") @@ -484,7 +484,7 @@ def test_burn_cli_document_invocation(tmp_path, capsys): def test_burn_cli_core_invocation(tmp_path, capsys): - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "burn.db" _, core_root = _ingest_and_distill(db, "html://cli-core") @@ -507,7 +507,7 @@ def test_burn_cli_core_invocation(tmp_path, capsys): def test_burn_cli_default_kind_is_providence(tmp_path, capsys): """Backwards compat: bare `burn --cache-key X` still works (no --kind).""" - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "burn.db" # Seed a providence record reusing the existing test_burn helper logic. @@ -552,7 +552,7 @@ def test_burn_cli_default_kind_is_providence(tmp_path, capsys): def test_burn_cli_document_returns_non_zero_on_refused(tmp_path, capsys): - from aborist.cli import build_parser + from arborist.cli import build_parser db = tmp_path / "burn.db" root = _ingest_one(db, "html://cli-refuse") diff --git a/tests/test_claim_lattice.py b/tests/test_claim_lattice.py index 8ab46ef..0f265fd 100644 --- a/tests/test_claim_lattice.py +++ b/tests/test_claim_lattice.py @@ -13,8 +13,8 @@ from pathlib import Path import pytest -from aborist.qa.dag import build_run_dag, verify_run_dag -from aborist.qa.evidence import ( +from arborist.qa.dag import build_run_dag, verify_run_dag +from arborist.qa.evidence import ( build_evidence_map, evidence_map_by_evidence_id, evidence_map_by_pointer_id, @@ -22,11 +22,11 @@ from aborist.qa.evidence import ( render_claim_lattice, render_evidence_map, ) -from aborist.qa.keys import governance_policy_hash -from aborist.qa.parse_claims import parse_pointer_claims -from aborist.qa.runner import DEFAULT_POLICY -from aborist.qa.query import DEFAULT_QUERY_POLICY -from aborist.qa.verify import ( +from arborist.qa.keys import governance_policy_hash +from arborist.qa.parse_claims import parse_pointer_claims +from arborist.qa.runner import DEFAULT_POLICY +from arborist.qa.query import DEFAULT_QUERY_POLICY +from arborist.qa.verify import ( ANSWER_MODES, DEFAULT_ANSWER_MODE, verify_claim_lattice, @@ -470,7 +470,7 @@ def test_run_dag_root_changes_when_parsed_lattice_changes(): def test_verifier_method_check_accepts_claim_lattice(tmp_path: Path): """Fresh shards include 'claim_lattice' in the verifier_method CHECK.""" - from aborist.store import connect + from arborist.store import connect db = tmp_path / "fresh.db" conn = connect(db) @@ -486,7 +486,7 @@ def test_verifier_method_check_accepts_claim_lattice(tmp_path: Path): def test_legacy_shard_migrates_in_place(tmp_path: Path): """An old shard with the pre-claim_lattice CHECK gets rebuilt on open AND keeps run_dag_root / run_dag_blob populated.""" - from aborist.store import connect + from arborist.store import connect db = tmp_path / "legacy.db" c = sqlite3.connect(db) @@ -803,12 +803,12 @@ def test_per_chunk_evidence_map_query_path(tmp_path): """A single source with three chunks should produce three evidence objects in the prompt-facing map — not one whole-doc entry.""" from typing import Iterator - from aborist.document import Document - from aborist.ingest import ingest_source - from aborist.qa.client import StubClient - from aborist.qa.query import DEFAULT_QUERY_POLICY, query - from aborist.source import Source - from aborist.store import connect + from arborist.document import Document + from arborist.ingest import ingest_source + from arborist.qa.client import StubClient + from arborist.qa.query import DEFAULT_QUERY_POLICY, query + from arborist.source import Source + from arborist.store import connect class FakeSource(Source): source_type = "test" @@ -872,7 +872,7 @@ def test_per_chunk_evidence_map_query_path(tmp_path): def test_chunk_query_relevance_scores_by_overlap(): - from aborist.qa.query import _chunk_query_relevance + from arborist.qa.query import _chunk_query_relevance qstem = {"brachiosaurus", "film"} # Many distinct + many mentions wins. @@ -895,7 +895,7 @@ def test_chunk_query_relevance_scores_by_overlap(): def test_chunk_query_relevance_stem_aware(): - from aborist.qa.query import _chunk_query_relevance + from arborist.qa.query import _chunk_query_relevance # Query has the plural "dinosaurs"; chunk has the singular "dinosaur". # The stem strip in _body_count_with_stem should still match. @@ -911,16 +911,16 @@ def test_query_orders_chunks_by_relevance_g03(tmp_path): of the document — G0.3 must promote it to E1 so a model that lazy-anchors on early pointers still cites the relevant chunk.""" from typing import Iterator - from aborist.document import Document - from aborist.ingest import ingest_source - from aborist.qa.client import StubClient - from aborist.qa.evidence import build_evidence_map - from aborist.qa.query import ( + from arborist.document import Document + from arborist.ingest import ingest_source + from arborist.qa.client import StubClient + from arborist.qa.evidence import build_evidence_map + from arborist.qa.query import ( DEFAULT_QUERY_POLICY, _load_doc_chunks, _chunk_query_relevance, ) - from aborist.qa.query import _stem_token_for_match, _title_query_tokens - from aborist.source import Source - from aborist.store import connect + from arborist.qa.query import _stem_token_for_match, _title_query_tokens + from arborist.source import Source + from arborist.store import connect class FakeSource(Source): source_type = "test" @@ -1066,7 +1066,7 @@ def test_lazy_anchor_signals_not_in_run_dag_payload(): signals recoverable from claim_statuses, and threading them in would make run_dag_root depend on the model's anchoring habits.""" import inspect as _inspect - from aborist.qa.dag import build_run_dag + from arborist.qa.dag import build_run_dag sig = _inspect.signature(build_run_dag) params = set(sig.parameters) assert "pointer_id_distribution" not in params @@ -1078,11 +1078,11 @@ def test_per_chunk_evidence_map_uses_distinct_chunk_roots(tmp_path): different chunks of the same source produce different evidence_ids even though they share source_root.""" from typing import Iterator - from aborist.document import Document - from aborist.ingest import ingest_source - from aborist.qa.query import _load_doc_chunks - from aborist.source import Source - from aborist.store import connect + from arborist.document import Document + from arborist.ingest import ingest_source + from arborist.qa.query import _load_doc_chunks + from arborist.source import Source + from arborist.store import connect class FakeSource(Source): source_type = "test" diff --git a/tests/test_cli_render.py b/tests/test_cli_render.py index df72379..b0952b0 100644 --- a/tests/test_cli_render.py +++ b/tests/test_cli_render.py @@ -1,4 +1,4 @@ -"""Pretty-print + ensure_ascii=False on `aborist query` output. +"""Pretty-print + ensure_ascii=False on `arborist query` output. The CLI default emits a human-readable render of the query result; ``--json`` switches to the raw record. Both paths must: @@ -12,7 +12,7 @@ import json import pytest -from aborist.cli import _render_query_human +from arborist.cli import _render_query_human # --------------------------------------------------------------------------- @@ -139,7 +139,7 @@ def test_render_includes_short_cache_key_with_pointer_to_json(): def test_unicode_round_trips_through_print_path(capsys): """Pin: a result with a Pokémon-style answer prints é, not \\u00e9. Uses argparse via build_parser to exercise the real CLI dispatch.""" - from aborist.cli import build_parser + from arborist.cli import build_parser parser = build_parser() # Build a fake result and call _cmd_query's render path indirectly. diff --git a/tests/test_compress.py b/tests/test_compress.py index 0e77bac..803f694 100644 --- a/tests/test_compress.py +++ b/tests/test_compress.py @@ -4,16 +4,16 @@ from __future__ import annotations import sqlite3 -from aborist.compress import ( +from arborist.compress import ( _MIN_COMPRESS_BYTES, _ZSTD_MAGIC, is_compressed, pack_chunk, unpack_chunk, ) -from aborist.ingest import ingest_source -from aborist.sources.wikipedia_xml import WikipediaXmlDump -from aborist.store import connect +from arborist.ingest import ingest_source +from arborist.sources.wikipedia_xml import WikipediaXmlDump +from arborist.store import connect def test_pack_passes_short_text_through_uncompressed(): @@ -107,7 +107,7 @@ def test_ingest_writes_compressed_for_large_chunks(tmp_path): fixture = tmp_path / "wp.xml" fixture.write_text(xml, encoding="utf-8") - db_path = tmp_path / "aborist.db" + db_path = tmp_path / "arborist.db" conn = connect(db_path) try: ingest_source(conn, WikipediaXmlDump(fixture)) @@ -131,7 +131,7 @@ def test_ingest_then_search_round_trip_finds_compressed_doc(tmp_path): """FTS5 still indexes plaintext; chunks.content stays compressed; reads that go through unpack_chunk reconstruct the original. The whole pipeline keeps working.""" - from aborist.search import FTS5Backend + from arborist.search import FTS5Backend big_body = "merkle providence wikipedia anarchism " * 200 # ~7.4 KB xml = f""" @@ -150,7 +150,7 @@ def test_ingest_then_search_round_trip_finds_compressed_doc(tmp_path): fixture = tmp_path / "wp.xml" fixture.write_text(xml, encoding="utf-8") - db_path = tmp_path / "aborist.db" + db_path = tmp_path / "arborist.db" conn = connect(db_path) try: ingest_source(conn, WikipediaXmlDump(fixture)) diff --git a/tests/test_concepts.py b/tests/test_concepts.py index 2f8f41c..279b6e3 100644 --- a/tests/test_concepts.py +++ b/tests/test_concepts.py @@ -13,15 +13,15 @@ from pathlib import Path import pytest -from aborist.concepts import invalidate_cache as _invalidate_cache -from aborist.concepts.query import invalidate_cache -from aborist.concepts.seed import seed_legacy_concepts -from aborist.qa.concepts import ( +from arborist.concepts import invalidate_cache as _invalidate_cache +from arborist.concepts.query import invalidate_cache +from arborist.concepts.seed import seed_legacy_concepts +from arborist.qa.concepts import ( has_compare_phrasing, rivalry_excluded, synonym_expand, ) -from aborist.store import connect +from arborist.store import connect # --------------------------------------------------------------------------- @@ -171,6 +171,6 @@ def test_invalidate_cache_does_not_break_lookup(seeded_shards): def test_export_invalidate_cache_alias(seeded_shards): """The package-level ``invalidate_cache`` is the same callable as - ``aborist.concepts.query.invalidate_cache`` — exposed at top level + ``arborist.concepts.query.invalidate_cache`` — exposed at top level for callers that don't want to import the implementation module.""" assert _invalidate_cache is invalidate_cache diff --git a/tests/test_dag.py b/tests/test_dag.py index adfaad9..395aa9c 100644 --- a/tests/test_dag.py +++ b/tests/test_dag.py @@ -13,7 +13,7 @@ exactly as recorded. from __future__ import annotations -from aborist.qa.dag import ( +from arborist.qa.dag import ( build_run_dag, localize_failure, preflight_node_hash, @@ -262,7 +262,7 @@ def test_preflight_node_hash_changes_with_policy_refs(): def test_preflight_node_hash_includes_node_version(): """node_version field is part of the hashed payload so a future schema bump (preflight-node-v2 etc.) invalidates legacy nodes.""" - from aborist.qa.dag import ( + from arborist.qa.dag import ( PREFLIGHT_NODE_VERSION, build_preflight_node_payload, ) @@ -373,7 +373,7 @@ def test_reject_run_dag_three_stage_shape(): question → preflight → final_label. Audit replay can read the stage list and tell instantly that this row is a preflight rejection (3 stages = reject path).""" - from aborist.qa.dag import build_reject_run_dag, preflight_node_hash + from arborist.qa.dag import build_reject_run_dag, preflight_node_hash pre = preflight_node_hash( question_state={"preflight_result": "PREFLIGHT_BLOCKED"}, quantifier={"intensity": "ALL", "is_broad": True, @@ -396,7 +396,7 @@ def test_reject_run_dag_root_changes_with_preflight_hash(): """Two reject runs that differ only in the preflight payload (e.g. different policy state at rejection time) must produce different run_dag_root values.""" - from aborist.qa.dag import build_reject_run_dag, preflight_node_hash + from arborist.qa.dag import build_reject_run_dag, preflight_node_hash pre_a = preflight_node_hash( question_state={"preflight_result": "PREFLIGHT_BLOCKED"}, quantifier={"intensity": "ALL"}, @@ -422,7 +422,7 @@ def test_reject_run_dag_round_trips_through_verify(): """3-stage reject DAG must verify the same way as the standard 7/9/8/10-stage shapes.""" import json - from aborist.qa.dag import build_reject_run_dag, preflight_node_hash + from arborist.qa.dag import build_reject_run_dag, preflight_node_hash pre = preflight_node_hash( question_state={"preflight_result": "PREFLIGHT_BLOCKED"}, quantifier={"intensity": "ALL"}, @@ -442,8 +442,8 @@ def test_reject_run_dag_round_trips_through_verify(): def test_extract_preflight_hash_from_blob_with_preflight(): """Pull preflight stage hash out of a persisted run_dag_blob.""" import json - from aborist.qa.dag import build_run_dag, preflight_node_hash - from aborist.qa.query import _extract_preflight_hash_from_blob + from arborist.qa.dag import build_run_dag, preflight_node_hash + from arborist.qa.query import _extract_preflight_hash_from_blob pre = preflight_node_hash( question_state={"preflight_result": "PREFLIGHT_OK"}, quantifier={"intensity": "SINGULAR"}, @@ -458,14 +458,14 @@ def test_extract_preflight_hash_returns_none_for_legacy_blob(): """Legacy blobs (no preflight stage) return None — this is the fall-through path for cache rows written before #000009.""" import json - from aborist.qa.query import _extract_preflight_hash_from_blob + from arborist.qa.query import _extract_preflight_hash_from_blob out = build_run_dag(**_kw()) # no preflight_hash blob = json.dumps(out, separators=(",", ":")) assert _extract_preflight_hash_from_blob(blob) is None def test_extract_preflight_hash_returns_none_for_empty_or_invalid(): - from aborist.qa.query import _extract_preflight_hash_from_blob + from arborist.qa.query import _extract_preflight_hash_from_blob assert _extract_preflight_hash_from_blob(None) is None assert _extract_preflight_hash_from_blob("") is None assert _extract_preflight_hash_from_blob("not json {{{") is None diff --git a/tests/test_directives.py b/tests/test_directives.py index 7942e28..86e9318 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -24,7 +24,7 @@ def test_d1_verify_quotes_signature_has_no_chat_client(): """The hard verifier never accepts a chat-client / LLM judge. A future PR that adds a chat_client parameter to verify_quotes would silently introduce a soft-signal-as-hard-check leak.""" - from aborist.qa.verify import verify_quotes + from arborist.qa.verify import verify_quotes sig = inspect.signature(verify_quotes) forbidden = {"chat_client", "client", "llm", "judge", "model"} @@ -37,7 +37,7 @@ def test_d1_verify_quotes_signature_has_no_chat_client(): def test_d1_verify_claim_lattice_signature_has_no_chat_client(): - from aborist.qa.verify import verify_claim_lattice + from arborist.qa.verify import verify_claim_lattice sig = inspect.signature(verify_claim_lattice) forbidden = {"chat_client", "client", "llm", "judge", "model"} @@ -49,7 +49,7 @@ def test_d1_verify_claim_lattice_signature_has_no_chat_client(): def test_d1_verify_claim_lattice_json_signature_has_no_chat_client(): - from aborist.qa.verify import verify_claim_lattice_json + from arborist.qa.verify import verify_claim_lattice_json sig = inspect.signature(verify_claim_lattice_json) forbidden = {"chat_client", "client", "llm", "judge", "model"} @@ -65,7 +65,7 @@ def test_d1_verifier_method_enum_excludes_llm_judges(): include any LLM-as-judge token. If a future migration adds `llm`, `model`, `judge`, or `nli` to the enum, the verifier started accepting soft signals into the hard chain.""" - from aborist.store import SCHEMA_SQL + from arborist.store import SCHEMA_SQL # Find the verifier_method CHECK constraint in SCHEMA_SQL. match = re.search( @@ -97,14 +97,14 @@ def test_d1_verifier_method_enum_excludes_llm_judges(): def test_d2_answer_modes_include_lattice_variants(): """ANSWER_MODES must contain both lattice variants so an agent can pick a pointer-clause-emitting mode.""" - from aborist.qa.verify import ANSWER_MODES + from arborist.qa.verify import ANSWER_MODES assert "claim_lattice_pointer" in ANSWER_MODES assert "claim_lattice" in ANSWER_MODES def test_d2_default_answer_mode_is_a_known_mode(): - from aborist.qa.verify import ANSWER_MODES, DEFAULT_ANSWER_MODE + from arborist.qa.verify import ANSWER_MODES, DEFAULT_ANSWER_MODE assert DEFAULT_ANSWER_MODE in ANSWER_MODES @@ -113,7 +113,7 @@ def test_d2_pointer_parser_exists_and_returns_claim_nodes(): """The pointer-line parser is the surface that turns model output into internal claim nodes. Its absence would mean Hermes has nowhere to emit pointer clauses to.""" - from aborist.qa.parse_claims import parse_pointer_claims + from arborist.qa.parse_claims import parse_pointer_claims sample = "Steve Jobs co-founded Apple. [E1]\n" out = parse_pointer_claims(sample) @@ -136,7 +136,7 @@ def test_d3_runtime_owns_claim_lattice_construction(): pointer mode — it produces prose that the runtime structures. A regression that asks the model for structured JSON in pointer mode would violate this directive.""" - from aborist.qa.parse_claims import parse_pointer_claims + from arborist.qa.parse_claims import parse_pointer_claims multi_line = ( "Steve Jobs co-founded Apple. [E1]\n" @@ -158,7 +158,7 @@ def test_d3_evidence_map_built_from_runtime_chunks_not_model_input(): chunks, not threaded in from the model's prompt-side invention. Two chunks → two evidence objects with deterministic pointer ids.""" - from aborist.qa.evidence import build_evidence_map + from arborist.qa.evidence import build_evidence_map chunks = [ { @@ -198,7 +198,7 @@ def test_d4_run_dag_carries_evidence_map_root(): """The 9-stage CTI run-DAG must commit the evidence_map_root. Without this, two runs whose retrieval surfaced different chunks would produce indistinguishable run_dag_roots.""" - from aborist.qa.dag import build_run_dag + from arborist.qa.dag import build_run_dag sig = inspect.signature(build_run_dag) assert "evidence_map_root" in sig.parameters, ( @@ -216,7 +216,7 @@ def test_d4_retrieval_plan_binding_landed(): Two runs with identical sources but different retrieval keywords produce different `run_dag_root` values — provenance closes the 'how did retrieval choose these sources' gap.""" - from aborist.qa.dag import build_run_dag + from arborist.qa.dag import build_run_dag sig = inspect.signature(build_run_dag) assert "retrieval_plan_hash" in sig.parameters, ( @@ -251,8 +251,8 @@ def test_d4_retrieval_plan_binding_landed(): def test_d4_retrieval_plan_hash_module_exists(): """RetrievalPlan dataclass + retrieval_plan_hash function landed - in `aborist.qa.retrieval_plan` per ticket #000001.""" - from aborist.qa.retrieval_plan import RetrievalPlan, retrieval_plan_hash + in `arborist.qa.retrieval_plan` per ticket #000001.""" + from arborist.qa.retrieval_plan import RetrievalPlan, retrieval_plan_hash plan = RetrievalPlan( retrieval_keywords="orwell 1984", @@ -285,8 +285,8 @@ def test_d5_verify_claim_lattice_is_deterministic(): """Same inputs → same verdict, byte-for-byte. No randomness, no time-dependent state, no hidden global. Regression here means the verifier started consulting some non-deterministic source.""" - from aborist.qa.evidence import build_evidence_map - from aborist.qa.verify import verify_claim_lattice + from arborist.qa.evidence import build_evidence_map + from arborist.qa.verify import verify_claim_lattice chunks = [{ "source_root": "f" * 64, @@ -310,7 +310,7 @@ def test_d5_seven_hard_checks_are_pure_functions(): """Spot check: the per-check helper for citation overlap (`_claim_textually_overlaps_evidence`) is a pure function — no network, no chat client, no global state.""" - from aborist.qa.verify import _claim_textually_overlaps_evidence + from arborist.qa.verify import _claim_textually_overlaps_evidence sig = inspect.signature(_claim_textually_overlaps_evidence) forbidden = {"chat_client", "client", "llm", "judge", "model"} @@ -326,7 +326,7 @@ def test_d6_warrant_check_exists_and_is_lexical(): """warrant_check must exist as a deterministic lexical check. A future replacement that imports an NLI model into the hard path would violate D6's 'before NLI' constraint.""" - from aborist.qa.warrant import warrant_check + from arborist.qa.warrant import warrant_check sig = inspect.signature(warrant_check) forbidden = {"chat_client", "client", "llm", "judge", "model", "nli"} @@ -342,7 +342,7 @@ def test_d6_warrant_fires_on_date_anchor_mismatch(): class. This case is the deterministic anchor (year strings are case-trivial; ALL-match semantics). Regression here means the date-anchor extractor or check silently broke.""" - from aborist.qa.warrant import warrant_check + from arborist.qa.warrant import warrant_check # Claim asserts "1985"; cited span has no 1985 anywhere. ok, missing = warrant_check( @@ -365,7 +365,7 @@ def test_d6_warrant_relation_shape_fires_when_all_anchors_missing(): extracted anchor must appear in some cited span. This test exercises a true failure where the claim's only proper-noun anchor is missing from every cited span.""" - from aborist.qa.warrant import warrant_check + from arborist.qa.warrant import warrant_check ok, missing = warrant_check( claim_text="Mr. Burns is the boss.", @@ -386,7 +386,7 @@ def test_d6_warrant_generalization_landed(): """Per-shape warrant detectors landed via ticket #000003. Entity-list / count / why-cause shapes now dispatch through warrant_check alongside the original relation + date classes.""" - import aborist.qa.warrant as warrant_mod + import arborist.qa.warrant as warrant_mod expected = ( "_question_is_entity_list_shape", @@ -416,7 +416,7 @@ def test_d7_renderer_relabels_strict_for_claim_lattice(): (POINTER-LINKED / ANCHOR-WARRANTED / EVIDENCE-WARRANTED) replaces the previous two-rung EVIDENCE-LINKED/EVIDENCE-LINKED-PARTIAL surface as of #000005.""" - from aborist.cli import _render_audit_label + from arborist.cli import _render_audit_label # No violations + STRICT → top rung. assert "EVIDENCE-WARRANTED" in _render_audit_label("STRICT", "claim_lattice", []) @@ -431,7 +431,7 @@ def test_d7_renderer_keeps_strict_for_pinned_span_methods(): """Quote / span / entity / paraphrase verify against pinned spans, not synthesis. STRICT is honest there; renderer must NOT apply the ladder relabel.""" - from aborist.cli import _render_audit_label + from arborist.cli import _render_audit_label for method in ("quote", "span", "entity", "paraphrase"): label = _render_audit_label("STRICT", method, []) @@ -447,7 +447,7 @@ def test_d7_audit_mode_enum_canonical_set(): """Schema column must keep the canonical 3-value enum so v9.8 cache_key invariants hold. Renderer-level relabel (above) doesn't touch this.""" - from aborist.store import SCHEMA_SQL + from arborist.store import SCHEMA_SQL match = re.search( r"audit_mode\s+TEXT[^,]+CHECK\s*\(\s*audit_mode\s+IN\s*\(([^)]+)\)\)", diff --git a/tests/test_distill.py b/tests/test_distill.py index 2c5e04d..d60fe86 100644 --- a/tests/test_distill.py +++ b/tests/test_distill.py @@ -5,13 +5,13 @@ from __future__ import annotations import json from typing import Iterator -from aborist.distill import FirstSentenceDistiller -from aborist.distill.runner import distill_existing -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.merkle import proof_from_dict, verify_proof -from aborist.source import Source -from aborist.store import connect +from arborist.distill import FirstSentenceDistiller +from arborist.distill.runner import distill_existing +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.merkle import proof_from_dict, verify_proof +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): diff --git a/tests/test_distill_recursive.py b/tests/test_distill_recursive.py index d73a57d..339c746 100644 --- a/tests/test_distill_recursive.py +++ b/tests/test_distill_recursive.py @@ -5,13 +5,13 @@ from __future__ import annotations import json from typing import Iterator -from aborist.distill import FirstSentenceDistiller -from aborist.distill.runner import distill_existing -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.merkle import proof_from_dict, verify_proof -from aborist.source import Source -from aborist.store import connect +from arborist.distill import FirstSentenceDistiller +from arborist.distill.runner import distill_existing +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.merkle import proof_from_dict, verify_proof +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): diff --git a/tests/test_evict.py b/tests/test_evict.py index 7724c95..b45c23b 100644 --- a/tests/test_evict.py +++ b/tests/test_evict.py @@ -5,13 +5,13 @@ from __future__ import annotations import json from typing import Iterator -from aborist.distill import FirstSentenceDistiller -from aborist.distill.runner import distill_existing -from aborist.document import Document -from aborist.evict import evict_to_cold, rehydrate -from aborist.ingest import ingest_source -from aborist.source import Source -from aborist.store import connect +from arborist.distill import FirstSentenceDistiller +from arborist.distill.runner import distill_existing +from arborist.document import Document +from arborist.evict import evict_to_cold, rehydrate +from arborist.ingest import ingest_source +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): @@ -226,7 +226,7 @@ def test_rehydrate_nothing_to_do_when_all_hot(tmp_path): def test_rehydrate_non_rehydratable_source(tmp_path): """A source_type without a registered fetcher (and no override) is honest about it.""" - from aborist.source import Source + from arborist.source import Source class WikiSource(Source): source_type = "wikipedia_cur" diff --git a/tests/test_frame.py b/tests/test_frame.py index ed15c45..239c933 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -7,7 +7,7 @@ indicators. """ from __future__ import annotations -from aborist.qa.frame import ( +from arborist.qa.frame import ( FrameDetection, _body_indicates_reference_work, _source_is_reference_work, diff --git a/tests/test_grok_source.py b/tests/test_grok_source.py index 0bba14b..9328867 100644 --- a/tests/test_grok_source.py +++ b/tests/test_grok_source.py @@ -13,15 +13,15 @@ from pathlib import Path import pytest -from aborist.ingest import ingest_source -from aborist.sources.grok import ( +from arborist.ingest import ingest_source +from arborist.sources.grok import ( GrokExportSource, GrokMediaPostsSource, _extract_urls, _normalize_ts, _scrub, ) -from aborist.store import connect +from arborist.store import connect def _make_export(tmp_path: Path) -> Path: @@ -224,7 +224,7 @@ def test_resolve_missing_path_raises(tmp_path): def test_grok_ingest_round_trip(tmp_path): """End-to-end: ingest a tiny export into a fresh DB, verify counts.""" root = _make_export(tmp_path) - db_path = tmp_path / "aborist.db" + db_path = tmp_path / "arborist.db" conn = connect(db_path) try: conv_stats = ingest_source(conn, GrokExportSource(path=root)) diff --git a/tests/test_html_source.py b/tests/test_html_source.py index b31e459..cb97eef 100644 --- a/tests/test_html_source.py +++ b/tests/test_html_source.py @@ -7,7 +7,7 @@ import pytest # Skip if optional extras are not installed. selectolax = pytest.importorskip("selectolax") -from aborist.sources.html_page import parse_html +from arborist.sources.html_page import parse_html SAMPLE_HTML = """ diff --git a/tests/test_ingest.py b/tests/test_ingest.py index 4fc8225..d3937de 100644 --- a/tests/test_ingest.py +++ b/tests/test_ingest.py @@ -4,12 +4,12 @@ from __future__ import annotations from typing import Iterator -from aborist.document import Document, Edge -from aborist.ingest import ingest_source, verify_random_sample -from aborist.search import FTS5Backend -from aborist.search.base import AuditMode -from aborist.source import Source -from aborist.store import connect, stats +from arborist.document import Document, Edge +from arborist.ingest import ingest_source, verify_random_sample +from arborist.search import FTS5Backend +from arborist.search.base import AuditMode +from arborist.source import Source +from arborist.store import connect, stats class FakeSource(Source): diff --git a/tests/test_inspect.py b/tests/test_inspect.py index f2f45de..4595cdd 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -1,6 +1,6 @@ """Sidecar diagnostic for unverified spans. -`aborist inspect --cache-key X` reads a providence_cache record, pulls +`arborist inspect --cache-key X` reads a providence_cache record, pulls the same source chunks the verifier saw, and classifies each unverified_quote into one of: verbatim_in_base, verbatim_in_raw_only, trailing_artifact, paraphrase, partial_paraphrase, no_overlap. @@ -18,7 +18,7 @@ from pathlib import Path import pytest -from aborist.qa.inspect import ( +from arborist.qa.inspect import ( _classify_span, _normalize, diagnose_deflection, @@ -26,7 +26,7 @@ from aborist.qa.inspect import ( diagnose_title_relevance, inspect_cache_key, ) -from aborist.store import append_audit, connect, transaction +from arborist.store import append_audit, connect, transaction # --------------------------------------------------------------------------- @@ -303,7 +303,7 @@ def _seed_doc(shard: Path, *, document_root: str, document_uri: str, """Insert a documents row + one hot chunk with the given text.""" conn = connect(shard) try: - from aborist.compress import pack_chunk + from arborist.compress import pack_chunk with transaction(conn): conn.execute( @@ -666,7 +666,7 @@ def test_metaphor_deflection_filters_common_ly_nouns(): """Naive .endswith('ly') would pick up 'butterfly', 'family', 'italy', 'july' etc. as adverbs. The block-list filters them so they don't inflate cue count.""" - from aborist.qa.inspect import _extract_metaphor_cues + from arborist.qa.inspect import _extract_metaphor_cues cues = _extract_metaphor_cues( "the butterfly flew over italy in july with the family" ) @@ -694,7 +694,7 @@ def test_metaphor_deflection_under_threshold_returns_no_signal(): def test_register_metaphor_dictionary_unions_into_wordlist(tmp_path): """Custom wordlist registers, unions into the default, and the suffix tests pick up domain-specific stems.""" - import aborist.qa.inspect as m + import arborist.qa.inspect as m # Reset cache so the test sees a clean slate. saved_cache = m._english_wordlist_cache @@ -722,10 +722,10 @@ def test_register_metaphor_dictionary_unions_into_wordlist(tmp_path): m._extra_dict_paths = saved_extra -def test_aborist_metaphor_dicts_env_var_supplements(monkeypatch, tmp_path): - """Setting ABORIST_METAPHOR_DICTS=path1:path2 unions both into +def test_arborist_metaphor_dicts_env_var_supplements(monkeypatch, tmp_path): + """Setting ARBORIST_METAPHOR_DICTS=path1:path2 unions both into the default wordlist on first lookup.""" - import aborist.qa.inspect as m + import arborist.qa.inspect as m saved_cache = m._english_wordlist_cache m._english_wordlist_cache = None @@ -735,7 +735,7 @@ def test_aborist_metaphor_dicts_env_var_supplements(monkeypatch, tmp_path): d1.write_text("widgetspeak\n") d2 = tmp_path / "d2.txt" d2.write_text("frizzlebop\n") - monkeypatch.setenv("ABORIST_METAPHOR_DICTS", f"{d1}:{d2}") + monkeypatch.setenv("ARBORIST_METAPHOR_DICTS", f"{d1}:{d2}") wl = m._english_wordlist() assert "widgetspeak" in wl @@ -747,7 +747,7 @@ def test_aborist_metaphor_dicts_env_var_supplements(monkeypatch, tmp_path): def test_register_metaphor_dictionary_idempotent(tmp_path): """Registering the same path twice is a no-op (cache invalidates once, second call is a no-op since path already in list).""" - import aborist.qa.inspect as m + import arborist.qa.inspect as m saved_extra = list(m._extra_dict_paths) saved_cache = m._english_wordlist_cache diff --git a/tests/test_journal.py b/tests/test_journal.py index 1e9cef0..31c3d83 100644 --- a/tests/test_journal.py +++ b/tests/test_journal.py @@ -5,7 +5,7 @@ The contract being tested is the unfirehose/1.0 schema: - one message per subsequent line - session_end system message on close - ``$schema: "unfirehose/1.0"`` on every record -- aborist-specific extras under namespaced ``aborist_meta`` +- arborist-specific extras under namespaced ``arborist_meta`` """ from __future__ import annotations @@ -13,7 +13,7 @@ from __future__ import annotations import json from pathlib import Path -from aborist.journal import ( +from arborist.journal import ( HARNESS_NAME, UNFIREHOSE_SCHEMA, SessionWriter, @@ -27,7 +27,7 @@ def _read_lines(path: Path) -> list[dict]: def test_slugify_cwd_basic(): - assert slugify_cwd("/home/fox/git/aborist") == "-home-fox-git-aborist" + assert slugify_cwd("/home/fox/git/arborist") == "-home-fox-git-arborist" def test_slugify_cwd_dots_become_hyphens(): @@ -84,15 +84,15 @@ def test_close_writes_session_end(tmp_path): assert last["durationMs"] >= 0 -def test_aborist_meta_passes_through(tmp_path): +def test_arborist_meta_passes_through(tmp_path): meta = {"audit_mode": "STRICT", "n_verified": 2, "cache_key": "abc123"} with SessionWriter(first_prompt="q", root=tmp_path) as s: - s.assistant_message("answer", aborist_meta=meta) + s.assistant_message("answer", arborist_meta=meta) rows = _read_lines(list(tmp_path.glob("**/*.jsonl"))[0]) asst = next(r for r in rows if r["type"] == "message" and r["role"] == "assistant") - assert asst["aborist_meta"]["audit_mode"] == "STRICT" - assert asst["aborist_meta"]["n_verified"] == 2 - assert asst["aborist_meta"]["cache_key"] == "abc123" + assert asst["arborist_meta"]["audit_mode"] == "STRICT" + assert asst["arborist_meta"]["n_verified"] == 2 + assert asst["arborist_meta"]["cache_key"] == "abc123" def test_session_id_used_as_filename(tmp_path): diff --git a/tests/test_keys.py b/tests/test_keys.py index 6f9b612..f10d3c5 100644 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -1,6 +1,6 @@ """Cache-key dimension hashes — pure functions, deterministic. -The 8-dim cache_key invariants are spread across `aborist/qa/keys.py`. +The 8-dim cache_key invariants are spread across `arborist/qa/keys.py`. Most behavior is covered indirectly by `test_qa.py` (cache hits, etc.); this file pins the hash-input canonicalization rules so a future tweak to question normalization can't silently change the equivalence class @@ -9,7 +9,7 @@ of "what counts as the same question." from __future__ import annotations -from aborist.qa.keys import ( +from arborist.qa.keys import ( cache_key, conversation_hash, governance_policy_hash, @@ -51,7 +51,7 @@ def test_question_hash_lowercases(): def test_question_hash_collapses_internal_spaces(): """canonicalize() collapses runs of spaces to a single space. Tabs/ newlines map to newline (a separate equivalence class — see - aborist.document.canonicalize), so tab vs space is intentionally + arborist.document.canonicalize), so tab vs space is intentionally NOT in the same bucket.""" assert question_hash("who is X") == question_hash("who is X") @@ -248,7 +248,7 @@ def test_verifier_policy_hash_only_hashes_verifier_subset(): governance_policy_hash is to surface verifier-rule changes independently from prompt / sampling-knob changes. """ - from aborist.qa.keys import verifier_policy_hash + from arborist.qa.keys import verifier_policy_hash base = { "answer_mode": "claim_lattice_pointer", @@ -273,7 +273,7 @@ def test_verifier_policy_hash_changes_when_verifier_field_changes(): of these means the verifier rules differ and a new cache record is required. """ - from aborist.qa.keys import verifier_policy_hash + from arborist.qa.keys import verifier_policy_hash base = { "answer_mode": "claim_lattice_pointer", @@ -296,7 +296,7 @@ def test_verifier_policy_hash_changes_when_verifier_field_changes(): def test_verifier_policy_hash_empty_policy_is_stable(): """Empty policy → constant hash. Stable across runs.""" - from aborist.qa.keys import verifier_policy_hash + from arborist.qa.keys import verifier_policy_hash assert verifier_policy_hash({}) == verifier_policy_hash({}) diff --git a/tests/test_llm_context_base.py b/tests/test_llm_context_base.py index b3281dc..32fa310 100644 --- a/tests/test_llm_context_base.py +++ b/tests/test_llm_context_base.py @@ -19,12 +19,12 @@ from pathlib import Path import pytest -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.qa.client import StubClient -from aborist.qa.runner import DEFAULT_POLICY, ask -from aborist.store import connect -from aborist.wikitext import BASE_VERSION +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.qa.client import StubClient +from arborist.qa.runner import DEFAULT_POLICY, ask +from arborist.store import connect +from arborist.wikitext import BASE_VERSION WIKITEXT_DOC = ( @@ -156,7 +156,7 @@ def test_base_version_changes_governance_hash(tmp_path): answers (under raw-wikitext policy) from satisfying lookups under the new (stripped) policy on the same question/source — they remain distinct cache_keys.""" - from aborist.qa.keys import governance_policy_hash + from arborist.qa.keys import governance_policy_hash p_with = dict(DEFAULT_POLICY) p_with["base_version"] = "wikitext-base-v1" diff --git a/tests/test_merkle.py b/tests/test_merkle.py index 63b18c3..73aed43 100644 --- a/tests/test_merkle.py +++ b/tests/test_merkle.py @@ -10,7 +10,7 @@ from __future__ import annotations import pytest -from aborist.merkle import ( +from arborist.merkle import ( HASH_LEN, MerkleTree, ZERO_HASH, @@ -117,7 +117,7 @@ def test_tampered_sibling_fails_verification(): leaves = [_leaf(f"chunk-{i}") for i in range(4)] tree = MerkleTree.build(leaves) proof = tree.proof(0) - from aborist.merkle import ProofNode + from arborist.merkle import ProofNode bad_siblings = list(proof.siblings) s0 = bad_siblings[0] @@ -136,7 +136,7 @@ def test_swapped_is_left_flag_fails(): leaves = [_leaf(f"chunk-{i}") for i in range(4)] tree = MerkleTree.build(leaves) proof = tree.proof(1) - from aborist.merkle import MerkleProof, ProofNode + from arborist.merkle import MerkleProof, ProofNode flipped = MerkleProof( leaf=proof.leaf, diff --git a/tests/test_mesh.py b/tests/test_mesh.py index 8e0b5e8..676b905 100644 --- a/tests/test_mesh.py +++ b/tests/test_mesh.py @@ -1,6 +1,6 @@ """Tests for the mesh layer: crypto primitives, state machine, eviction. -Mesh state lives in the standard aborist DB. These tests build a fresh +Mesh state lives in the standard arborist DB. These tests build a fresh DB per test via tmp_path so cross-test contamination is impossible. """ @@ -10,7 +10,7 @@ import os import pytest -from aborist.mesh import ( +from arborist.mesh import ( aead_decrypt, aead_encrypt, current_epoch, @@ -24,13 +24,13 @@ from aborist.mesh import ( sign, verify, ) -from aborist.mesh.members import add_member, kick_member, scheduled_rotate -from aborist.mesh.state import ( +from arborist.mesh.members import add_member, kick_member, scheduled_rotate +from arborist.mesh.state import ( MeshRosterEntry, roster_at, unwrap_secret_for_self, ) -from aborist.store import connect +from arborist.store import connect # --------------------------------------------------------------------------- diff --git a/tests/test_mesh_aead.py b/tests/test_mesh_aead.py index 33a1436..f767c6e 100644 --- a/tests/test_mesh_aead.py +++ b/tests/test_mesh_aead.py @@ -21,10 +21,10 @@ from pathlib import Path import pytest -from aborist.mesh import init_identity -from aborist.mesh.members import add_member -from aborist.mesh.state import recover_epoch_secret -from aborist.mesh.wire import ( +from arborist.mesh import init_identity +from arborist.mesh.members import add_member +from arborist.mesh.state import recover_epoch_secret +from arborist.mesh.wire import ( MeshWireServer, TYPE_ANNOUNCE_FALSIFICATION, TYPE_ANNOUNCE_ROOT, @@ -32,7 +32,7 @@ from aborist.mesh.wire import ( decrypt_envelope_body, encrypt_body_for_envelope, ) -from aborist.store import connect +from arborist.store import connect def _bootstrap_two_peer_db(tmp_path: Path) -> tuple[Path, Path, dict, dict]: @@ -55,7 +55,7 @@ def _bootstrap_two_peer_db(tmp_path: Path) -> tuple[Path, Path, dict, dict]: import json as _json import sqlite3 as _sqlite3 - from aborist.store import append_audit, latest_event_hash + from arborist.store import append_audit, latest_event_hash alice_db = tmp_path / "alice.db" bob_db = tmp_path / "bob.db" @@ -409,7 +409,7 @@ def test_mixed_cleartext_and_encrypted_round_trip(tmp_path): """Alice sends one cleartext ANNOUNCE_ROOT, then one encrypted ANNOUNCE_FALSIFICATION. Bob accepts both; audit chain logs the `encrypted` flag accurately on each.""" - from aborist.mesh.wire import MeshWireClient + from arborist.mesh.wire import MeshWireClient alice_db, bob_db, _alice_id, _bob_id = _bootstrap_two_peer_db(tmp_path) bob_srv = MeshWireServer(bob_db, host="127.0.0.1", port=0) @@ -463,7 +463,7 @@ def test_cleartext_default_envelope_unchanged(tmp_path): identically to the v1 wire format — no `encrypted_body` field on the canonical bytes, no signature change, no audit-body change. """ - from aborist.mesh.wire import MeshWireClient + from arborist.mesh.wire import MeshWireClient alice_db, bob_db, _alice_id, _bob_id = _bootstrap_two_peer_db(tmp_path) bob_srv = MeshWireServer(bob_db, host="127.0.0.1", port=0) diff --git a/tests/test_mesh_chain.py b/tests/test_mesh_chain.py index 50f56b0..2352782 100644 --- a/tests/test_mesh_chain.py +++ b/tests/test_mesh_chain.py @@ -21,15 +21,15 @@ from pathlib import Path import pytest -from aborist.mesh import init_identity -from aborist.mesh.crypto import sign as _sign -from aborist.mesh.members import add_member -from aborist.mesh.wire import ( +from arborist.mesh import init_identity +from arborist.mesh.crypto import sign as _sign +from arborist.mesh.members import add_member +from arborist.mesh.wire import ( MeshWireServer, TYPE_ANNOUNCE_ROOT, WireEnvelope, ) -from aborist.store import connect +from arborist.store import connect def _bootstrap_three_peer_db(tmp_path: Path): @@ -40,7 +40,7 @@ def _bootstrap_three_peer_db(tmp_path: Path): roster at ``current_epoch`` contains bob and carol so signature verification finds their sign_pubs when receiving from either. """ - from aborist.mesh.state import current_epoch as _current_epoch + from arborist.mesh.state import current_epoch as _current_epoch alice_db = tmp_path / "alice.db" bob_db = tmp_path / "bob.db" diff --git a/tests/test_mesh_cli.py b/tests/test_mesh_cli.py index 67d8dd2..24a1daf 100644 --- a/tests/test_mesh_cli.py +++ b/tests/test_mesh_cli.py @@ -1,4 +1,4 @@ -"""CLI integration: `aborist mesh serve` and `aborist mesh sync`. +"""CLI integration: `arborist mesh serve` and `arborist mesh sync`. Wire-layer protocol behavior is covered in test_mesh_wire.py and test_mesh_wire_e2e.py — these tests just exercise the argparse plumbing, @@ -11,13 +11,13 @@ from __future__ import annotations import json from pathlib import Path -from aborist.cli import build_parser -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.mesh import init_identity, set_enabled -from aborist.mesh.members import add_member -from aborist.mesh.wire import MeshWireServer -from aborist.store import connect +from arborist.cli import build_parser +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.mesh import init_identity, set_enabled +from arborist.mesh.members import add_member +from arborist.mesh.wire import MeshWireServer +from arborist.store import connect class _OneDocSource: @@ -166,7 +166,7 @@ def test_sync_announces_falsifications(tmp_path, capsys): the falsification. Alice's audit chain gains one mesh_received event whose body carries an ANNOUNCE_FALSIFICATION envelope.""" import time as _time - from aborist.store import append_audit, transaction + from arborist.store import append_audit, transaction alice_db, bob_db, _alice, _bob = _bootstrap_two_peers(tmp_path) @@ -232,7 +232,7 @@ def test_sync_no_falsifications_flag_skips_them(tmp_path, capsys): """--no-falsifications skips the falsification broadcast. Bob's falsification is NOT announced. Alice gets only the root broadcasts.""" import time as _time - from aborist.store import append_audit, transaction + from arborist.store import append_audit, transaction alice_db, bob_db, _alice, _bob = _bootstrap_two_peers(tmp_path) b_conn = connect(bob_db) @@ -322,7 +322,7 @@ def test_sync_kindergarten_holds_fresh_records(tmp_path, capsys): A document ingested 30 seconds ago must NOT broadcast under the default 1-hour kindergarten window.""" import time as _time - from aborist.store import transaction + from arborist.store import transaction alice_db, bob_db, _alice, _bob = _bootstrap_two_peers(tmp_path) _ingest(bob_db, uri="https://example.com/old", content="Old prose.", title="Old") diff --git a/tests/test_mesh_cli_pull.py b/tests/test_mesh_cli_pull.py index f6874af..bb84d2a 100644 --- a/tests/test_mesh_cli_pull.py +++ b/tests/test_mesh_cli_pull.py @@ -1,4 +1,4 @@ -"""CLI integration: `aborist mesh pull`. +"""CLI integration: `arborist mesh pull`. Closes the request half of the gossip loop. Wire-layer Merkle / signature verification is covered in test_mesh_wire_e2e.py — these tests cover the @@ -11,13 +11,13 @@ from __future__ import annotations import json from pathlib import Path -from aborist.cli import build_parser -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.mesh import init_identity, set_enabled -from aborist.mesh.members import add_member -from aborist.mesh.wire import MeshWireServer -from aborist.store import connect +from arborist.cli import build_parser +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.mesh import init_identity, set_enabled +from arborist.mesh.members import add_member +from arborist.mesh.wire import MeshWireServer +from arborist.store import connect class _OneDocSource: diff --git a/tests/test_mesh_wire.py b/tests/test_mesh_wire.py index 3e9cb35..467e62e 100644 --- a/tests/test_mesh_wire.py +++ b/tests/test_mesh_wire.py @@ -11,13 +11,13 @@ from pathlib import Path import pytest -from aborist.mesh import init_identity -from aborist.mesh.members import add_member -from aborist.mesh.crypto import ( +from arborist.mesh import init_identity +from arborist.mesh.members import add_member +from arborist.mesh.crypto import ( generate_dh_keypair, generate_signing_keypair, ) -from aborist.mesh.wire import ( +from arborist.mesh.wire import ( ALL_TYPES, PATH_ANNOUNCE, PATH_INFO, @@ -30,7 +30,7 @@ from aborist.mesh.wire import ( WireEnvelope, verify_envelope_sig, ) -from aborist.store import connect +from arborist.store import connect def _bootstrap_two_peer_db(tmp_path: Path) -> tuple[Path, Path, dict, dict]: @@ -205,7 +205,7 @@ def test_verify_envelope_sig_accepts_known_member(tmp_path): body={"document_root": "11" * 32, "source_uri": "https://x"}, ) sig_payload = env.canonical_bytes() - from aborist.mesh.crypto import sign as _sign + from arborist.mesh.crypto import sign as _sign sig = _sign(alice_id.sign_priv, sig_payload) a_conn = connect(alice_db) @@ -225,7 +225,7 @@ def test_verify_envelope_sig_rejects_tampered_body(tmp_path): ts=100, body={"document_root": "aa" * 32, "source_uri": "https://x"}, ) - from aborist.mesh.crypto import sign as _sign + from arborist.mesh.crypto import sign as _sign sig = _sign(alice_id.sign_priv, env_orig.canonical_bytes()) env_tampered = WireEnvelope( @@ -252,7 +252,7 @@ def test_verify_envelope_sig_rejects_unknown_sender(tmp_path): ts=0, body={"document_root": "00" * 32, "source_uri": "https://x"}, ) - from aborist.mesh.crypto import sign as _sign + from arborist.mesh.crypto import sign as _sign sig = _sign(sign_priv, env.canonical_bytes()) a_conn = connect(alice_db) try: @@ -272,7 +272,7 @@ def test_verify_envelope_sig_rejects_wrong_key_for_known_member(tmp_path): ts=0, body={"document_root": "00" * 32, "source_uri": "https://x"}, ) - from aborist.mesh.crypto import sign as _sign + from arborist.mesh.crypto import sign as _sign sig = _sign(rogue_priv, env.canonical_bytes()) a_conn = connect(alice_db) try: @@ -288,7 +288,7 @@ def test_verify_envelope_sig_rejects_wrong_key_for_known_member(tmp_path): def test_handle_announce_writes_audit_event(tmp_path): """Bob sends ANNOUNCE_ROOT to Alice's server; Alice's chain gains an event.""" - from aborist.mesh.wire import MeshWireServer + from arborist.mesh.wire import MeshWireServer alice_db, _bob_db, _alice_id, bob_id = _bootstrap_two_peer_db(tmp_path) @@ -340,7 +340,7 @@ def test_handle_announce_writes_audit_event(tmp_path): def test_handle_announce_rejects_bad_sig(tmp_path): """Sig from wrong key → 401, no audit event.""" - from aborist.mesh.wire import MeshWireServer + from arborist.mesh.wire import MeshWireServer alice_db, _bob_db, _alice_id, _bob_id = _bootstrap_two_peer_db(tmp_path) rogue_priv, _ = generate_signing_keypair() @@ -352,7 +352,7 @@ def test_handle_announce_rejects_bad_sig(tmp_path): ts=0, body={"document_root": "00" * 32, "source_uri": "x"}, ) - from aborist.mesh.crypto import sign as _sign + from arborist.mesh.crypto import sign as _sign sig = _sign(rogue_priv, env.canonical_bytes()) import base64 as _b64 signed = {"envelope": env.__dict__, "sig_b64": _b64.b64encode(sig).decode()} @@ -381,7 +381,7 @@ def test_handle_announce_rejects_bad_sig(tmp_path): def test_handle_announce_rejects_request_body_type(tmp_path): """The /announce endpoint refuses REQUEST_BODY / DELIVER_BODY.""" - from aborist.mesh.wire import MeshWireServer + from arborist.mesh.wire import MeshWireServer alice_db, _bob_db, _alice_id, bob_id = _bootstrap_two_peer_db(tmp_path) env = WireEnvelope( @@ -404,7 +404,7 @@ def test_handle_announce_rejects_request_body_type(tmp_path): def test_handle_request_404_on_unknown_root(tmp_path): """Asking for a doc the responder doesn't have returns 404 cleanly.""" - from aborist.mesh.wire import MeshWireServer + from arborist.mesh.wire import MeshWireServer alice_db, _bob_db, _alice_id, bob_id = _bootstrap_two_peer_db(tmp_path) env = WireEnvelope( @@ -427,7 +427,7 @@ def test_handle_request_404_on_unknown_root(tmp_path): def test_announce_falsification_round_trips(tmp_path): """Falsifications propagate as ANNOUNCE_FALSIFICATION.""" - from aborist.mesh.wire import MeshWireServer + from arborist.mesh.wire import MeshWireServer alice_db, _bob_db, _alice_id, bob_id = _bootstrap_two_peer_db(tmp_path) env = WireEnvelope( diff --git a/tests/test_mesh_wire_e2e.py b/tests/test_mesh_wire_e2e.py index 89c7a4e..101c2e4 100644 --- a/tests/test_mesh_wire_e2e.py +++ b/tests/test_mesh_wire_e2e.py @@ -17,16 +17,16 @@ from pathlib import Path import pytest -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.mesh import init_identity -from aborist.mesh.members import add_member -from aborist.mesh.wire import ( +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.mesh import init_identity +from arborist.mesh.members import add_member +from arborist.mesh.wire import ( MeshWireClient, MeshWireServer, TYPE_ANNOUNCE_ROOT, ) -from aborist.store import connect +from arborist.store import connect @pytest.fixture @@ -180,7 +180,7 @@ def test_request_body_rejects_tampered_response(two_peers, monkeypatch): title="X", ) with MeshWireClient(two_peers["bob"]["db"], two_peers["alice"]["srv"].url) as c: - from aborist.mesh import wire as _wire + from arborist.mesh import wire as _wire monkeypatch.setattr(_wire, "_merkle_root_matches", lambda *a, **k: False) with pytest.raises(ValueError, match="Merkle-derive"): c.request_body(root=document_root) diff --git a/tests/test_metacognition.py b/tests/test_metacognition.py index 9e1b099..1fd1fec 100644 --- a/tests/test_metacognition.py +++ b/tests/test_metacognition.py @@ -10,7 +10,7 @@ from __future__ import annotations import pytest -from aborist.qa.metacognition import ( +from arborist.qa.metacognition import ( PREFLIGHT_VERSION, QuestionState, detect_contradiction, @@ -305,12 +305,12 @@ def test_question_state_is_serializable(): def test_metacognition_field_is_in_verifier_policy_fields(field): """All six policy fields must be in _VERIFIER_POLICY_FIELDS so flipping any of them invalidates prior cache records on lookup.""" - from aborist.qa.keys import _VERIFIER_POLICY_FIELDS + from arborist.qa.keys import _VERIFIER_POLICY_FIELDS assert field in _VERIFIER_POLICY_FIELDS def test_governance_hash_changes_when_metacognition_enabled_flips(): - from aborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash + from arborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash base = dict.fromkeys(_VERIFIER_POLICY_FIELDS, "default") base["metacognition_enabled"] = True h_on = verifier_policy_hash(base) @@ -320,7 +320,7 @@ def test_governance_hash_changes_when_metacognition_enabled_flips(): def test_governance_hash_changes_when_block_on_contradiction_flips(): - from aborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash + from arborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash base = dict.fromkeys(_VERIFIER_POLICY_FIELDS, "default") base["metacognition_block_on_contradiction"] = False h_off = verifier_policy_hash(base) @@ -332,8 +332,8 @@ def test_governance_hash_changes_when_block_on_contradiction_flips(): def test_default_policy_has_metacognition_enabled(): """Master switch default-on per ticket #000010 §7.3 — detectors are pure-on-question so the cost is negligible.""" - from aborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY assert RUNNER_POLICY["metacognition_enabled"] is True assert DEFAULT_QUERY_POLICY["metacognition_enabled"] is True @@ -341,8 +341,8 @@ def test_default_policy_has_metacognition_enabled(): def test_default_policy_block_on_contradiction_off(): """Default to label-only on contradictions. False-positive risk not yet bench-validated; opt-in via --block-on-contradiction.""" - from aborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY assert RUNNER_POLICY["metacognition_block_on_contradiction"] is False assert DEFAULT_QUERY_POLICY["metacognition_block_on_contradiction"] is False @@ -360,7 +360,7 @@ def _result_with_question_state(state: QuestionState) -> dict: def test_tail_renders_false_premise(): - from aborist.cli import _render_warrant_tail + from arborist.cli import _render_warrant_tail state = preflight_question( "When did Mr. Burns become Homer's biological father?" ) @@ -369,28 +369,28 @@ def test_tail_renders_false_premise(): def test_tail_renders_contradictory(): - from aborist.cli import _render_warrant_tail + from arborist.cli import _render_warrant_tail state = preflight_question("Which unmarried spouse is Homer married to?") tail = _render_warrant_tail(_result_with_question_state(state)) assert "contradictory" in tail def test_tail_renders_stale_risk(): - from aborist.cli import _render_warrant_tail + from arborist.cli import _render_warrant_tail state = preflight_question("Who is the current CEO of OpenAI?") tail = _render_warrant_tail(_result_with_question_state(state)) assert "stale risk" in tail def test_tail_renders_out_of_corpus(): - from aborist.cli import _render_warrant_tail + from arborist.cli import _render_warrant_tail state = preflight_question("What does my uploaded contract say?") tail = _render_warrant_tail(_result_with_question_state(state)) assert "out of corpus" in tail def test_tail_omits_metacog_when_well_formed(): - from aborist.cli import _render_warrant_tail + from arborist.cli import _render_warrant_tail state = preflight_question("what is the capital of france?") tail = _render_warrant_tail(_result_with_question_state(state)) # well_formed should NOT produce any metacog tail tokens. @@ -403,7 +403,7 @@ def test_tail_omits_metacog_when_well_formed(): def test_tail_combines_metacog_with_existing_kinds(): """Multiple tails compose: a TITLE_MISMATCH from the verifier plus a stale_risk from the preflight should both surface.""" - from aborist.cli import _render_warrant_tail + from arborist.cli import _render_warrant_tail state = preflight_question("Who is the current CEO of OpenAI?") result = _result_with_question_state(state) result["violations"] = [{"kind": "TITLE_MISMATCH"}] diff --git a/tests/test_migration_audit_mode.py b/tests/test_migration_audit_mode.py index 0263762..1816495 100644 --- a/tests/test_migration_audit_mode.py +++ b/tests/test_migration_audit_mode.py @@ -14,7 +14,7 @@ from __future__ import annotations import sqlite3 -from aborist.store import connect +from arborist.store import connect # Pre-v9.8-audit-mode providence_cache (snapshot of the schema before the diff --git a/tests/test_providence_source.py b/tests/test_providence_source.py index ebd3baa..f3d0932 100644 --- a/tests/test_providence_source.py +++ b/tests/test_providence_source.py @@ -9,7 +9,7 @@ docs/self-reference-design.md: 4. anti-recursion: records whose answer cites a self-reference URI are excluded (first-generation only) -Plus the classifier-side: documents with `aborist://providence/` +Plus the classifier-side: documents with `arborist://providence/` URIs classify as `self_reference_source`. """ @@ -20,12 +20,12 @@ import time import pytest -from aborist.qa.query import _classify_source_role -from aborist.sources.providence import ( +from arborist.qa.query import _classify_source_role +from arborist.sources.providence import ( PROVIDENCE_URI_PREFIX, ProvidenceSource, ) -from aborist.store import connect +from arborist.store import connect def _seed(conn: sqlite3.Connection, **fields) -> str: @@ -160,7 +160,7 @@ def test_kindergarten_zero_admits_all_strict_live(tmp_path): def test_anti_recursion_excludes_self_referencing_records(tmp_path): """A STRICT record whose own answer text contains - `aborist://providence/...` is excluded — first-generation only. + `arborist://providence/...` is excluded — first-generation only. Prevents echo-chamber chains where a wrong-but-STRICT record keeps getting recompiled into deeper claims.""" db = tmp_path / "p.db" diff --git a/tests/test_qa.py b/tests/test_qa.py index de28a75..681d6f1 100644 --- a/tests/test_qa.py +++ b/tests/test_qa.py @@ -9,20 +9,20 @@ from __future__ import annotations import hashlib from typing import Iterator -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.merkle import proof_from_dict, verify_proof -from aborist.qa import ask -from aborist.qa.client import StubClient -from aborist.qa.keys import ( +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.merkle import proof_from_dict, verify_proof +from arborist.qa import ask +from arborist.qa.client import StubClient +from arborist.qa.keys import ( cache_key, conversation_hash, governance_policy_hash, model_profile_hash, question_hash, ) -from aborist.source import Source -from aborist.store import connect +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): @@ -195,7 +195,7 @@ def test_unknown_document(tmp_path): def test_cold_source_refuses(tmp_path): """Source must be hot — answer derived from evicted content can't be proved.""" - from aborist.evict import evict_to_cold + from arborist.evict import evict_to_cold db = tmp_path / "cold.db" conn = connect(db) diff --git a/tests/test_qa_quality_live.py b/tests/test_qa_quality_live.py index 65f8ce2..e1d82b4 100644 --- a/tests/test_qa_quality_live.py +++ b/tests/test_qa_quality_live.py @@ -2,9 +2,9 @@ These tests are deliberately gated — they hit ``hermes.ai.unturf.com/v1`` and require a populated shard set under -``~/.aborist/shards`` (default) or the path passed via -``ABORIST_LIVE_SHARDS_DIR``. Default `make test` does NOT run these; -opt in via ``ABORIST_LIVE_TESTS=1`` (the ``make test-live`` target +``~/.arborist/shards`` (default) or the path passed via +``ARBORIST_LIVE_SHARDS_DIR``. Default `make test` does NOT run these; +opt in via ``ARBORIST_LIVE_TESTS=1`` (the ``make test-live`` target sets it). Why this exists: @@ -44,24 +44,24 @@ from pathlib import Path import pytest -_LIVE_OPT_IN = os.environ.get("ABORIST_LIVE_TESTS") == "1" +_LIVE_OPT_IN = os.environ.get("ARBORIST_LIVE_TESTS") == "1" _SHARDS_DIR = Path( - os.environ.get("ABORIST_LIVE_SHARDS_DIR") - or (Path.home() / ".aborist" / "shards") + os.environ.get("ARBORIST_LIVE_SHARDS_DIR") + or (Path.home() / ".arborist" / "shards") ) _QA_DB = _SHARDS_DIR / "qa.db" _ENDPOINT = os.environ.get( - "ABORIST_LLM_ENDPOINT", "https://hermes.ai.unturf.com/v1" + "ARBORIST_LLM_ENDPOINT", "https://hermes.ai.unturf.com/v1" ) _MODEL = os.environ.get( - "ABORIST_LLM_MODEL", + "ARBORIST_LLM_MODEL", "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic", ) pytestmark = pytest.mark.skipif( not _LIVE_OPT_IN, - reason="live QA tests gated by ABORIST_LIVE_TESTS=1; " + reason="live QA tests gated by ARBORIST_LIVE_TESTS=1; " "run via `make test-live` or set the env var", ) @@ -75,10 +75,10 @@ def _ask(question: str, *, mode: str = "claim_lattice", top_k: int = 8) -> dict: # Lazy import so collecting the file doesn't pull qa modules in # default-skip mode. - from aborist.qa.client import OpenAICompatibleClient - from aborist.qa.query import DEFAULT_QUERY_POLICY, query + from arborist.qa.client import OpenAICompatibleClient + from arborist.qa.query import DEFAULT_QUERY_POLICY, query - api_key = os.environ.get("ABORIST_LLM_API_KEY") + api_key = os.environ.get("ARBORIST_LLM_API_KEY") client = OpenAICompatibleClient(base_url=_ENDPOINT, api_key=api_key) policy = dict(DEFAULT_QUERY_POLICY) policy["answer_mode"] = mode diff --git a/tests/test_quantifier_caps.py b/tests/test_quantifier_caps.py index 488899a..c5c93af 100644 --- a/tests/test_quantifier_caps.py +++ b/tests/test_quantifier_caps.py @@ -1,7 +1,7 @@ """Quantifier cap table + governance hash + dry-run discipline. Ticket #000008 Phase 2: per-model cap profiles -(``aborist/qa/model_profiles.py``) and the four policy fields that +(``arborist/qa/model_profiles.py``) and the four policy fields that gate cap behavior: - ``quantifier_guard_enabled`` — master kill @@ -23,8 +23,8 @@ from __future__ import annotations import pytest -from aborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash -from aborist.qa.model_profiles import ( +from arborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash +from arborist.qa.model_profiles import ( EXPLICIT_COUNT, PROFILES, cap_for_intensity, @@ -230,8 +230,8 @@ def test_default_reminder_enabled_for_lattice_modes(): eliminates FORMAT_COLLAPSED, reduces NO_EVIDENCE_POINTER 33%, boosts mean ratio +17pp pointer / +21pp JSON, rescues JSON UNGROUNDED 7→1.""" - from aborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY assert RUNNER_POLICY["quantifier_reminder_enabled"] is True assert DEFAULT_QUERY_POLICY["quantifier_reminder_enabled"] is True @@ -242,8 +242,8 @@ def test_default_apply_caps_modes_is_json_only(): TOO_MANY_CLAIMS 20× without moving the 0/45 STRICT floor. Default the apply-caps allowlist to JSON-only so flipping apply_caps=True doesn't add wasted cap-noise on pointer mode.""" - from aborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY assert RUNNER_POLICY["quantifier_apply_caps_modes"] == ["claim_lattice"] assert DEFAULT_QUERY_POLICY["quantifier_apply_caps_modes"] == ["claim_lattice"] @@ -271,7 +271,7 @@ def test_apply_caps_default_off_preserves_dry_run_discipline(): even with reminder default-on. Dry-run discipline (§10.11.3) survives the §12 bench cycle — operators flip apply_caps via --apply-quantifier-caps after their own bench review.""" - from aborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.runner import DEFAULT_POLICY as RUNNER_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY assert RUNNER_POLICY["quantifier_guard_apply_caps"] is False assert DEFAULT_QUERY_POLICY["quantifier_guard_apply_caps"] is False diff --git a/tests/test_quantifier_classifier.py b/tests/test_quantifier_classifier.py index 5803f69..3a44d26 100644 --- a/tests/test_quantifier_classifier.py +++ b/tests/test_quantifier_classifier.py @@ -14,7 +14,7 @@ from __future__ import annotations import pytest -from aborist.qa.quantifier import ( +from arborist.qa.quantifier import ( CLASSIFIER_VERSION, classify_question_quantifier, ) diff --git a/tests/test_quantifier_phase4.py b/tests/test_quantifier_phase4.py index 0c8991f..22fc376 100644 --- a/tests/test_quantifier_phase4.py +++ b/tests/test_quantifier_phase4.py @@ -17,13 +17,13 @@ from __future__ import annotations import pytest -from aborist.cli import ( +from arborist.cli import ( _SOFT_DEMOTE_VIOLATION_KINDS, _ladder_rung_for_lattice, _render_query_human, _render_warrant_tail, ) -from aborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash +from arborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash # ---------------------------------------------------------------- soft-demote registration diff --git a/tests/test_quantifier_reminder.py b/tests/test_quantifier_reminder.py index 34e42e9..42f60f4 100644 --- a/tests/test_quantifier_reminder.py +++ b/tests/test_quantifier_reminder.py @@ -15,8 +15,8 @@ from __future__ import annotations import pytest -from aborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash -from aborist.qa.quantifier_reminder import broad_quantifier_reminder +from arborist.qa.keys import _VERIFIER_POLICY_FIELDS, verifier_policy_hash +from arborist.qa.quantifier_reminder import broad_quantifier_reminder # ---------------------------------------------------------------- gating diff --git a/tests/test_query.py b/tests/test_query.py index 03e92e7..e5980cf 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -12,12 +12,12 @@ from __future__ import annotations import json from typing import Iterator -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.qa import query -from aborist.qa.client import StubClient -from aborist.source import Source -from aborist.store import connect +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.qa import query +from arborist.qa.client import StubClient +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): @@ -339,7 +339,7 @@ def test_classify_source_role_separates_primary_from_noisy(): be secondary; The Lost World should be sequel; off-topic background. Catches the case where peripheral pages with strong title overlap used to share the primary slot.""" - from aborist.qa.query import _classify_source_role + from arborist.qa.query import _classify_source_role qstems = {"dinosaur", "jurassic", "park", "film"} assert _classify_source_role("Jurassic Park (film)", qstems) == "primary_answer_source" @@ -378,7 +378,7 @@ def test_query_persists_run_dag_root_on_record_and_result(tmp_path): """Per-run Merkle-DAG fingerprint surfaces on both the result dict & the persisted providence_cache row. Recomputing the root from the persisted blob matches what was stored.""" - from aborist.qa.dag import verify_run_dag + from arborist.qa.dag import verify_run_dag main_db = tmp_path / "corpus.db" qa_db = tmp_path / "qa.db" @@ -561,7 +561,7 @@ def test_query_burn_existing_forces_fresh_inference(tmp_path): def test_query_burn_existing_writes_audit_event(tmp_path): """Each --burn writes a providence_burn audit event so the chain records the bust. Verifies one event lands per burn.""" - from aborist.store import connect as _connect + from arborist.store import connect as _connect main_db = tmp_path / "corpus.db" qa_db = tmp_path / "qa.db" @@ -947,7 +947,7 @@ def test_question_phrases_returns_sliding_n_grams_no_stopword_strip(): """`_question_phrases` extracts verbatim n-token windows. Function words are kept — diagnostic value of an allusion is the EXACT sequence ('always been at war' >> 'always war').""" - from aborist.qa.query import _question_phrases + from arborist.qa.query import _question_phrases out = _question_phrases("has oceania always been at war with east asia", n=4) # 9-token query, 4-gram window → 6 phrases, all preserved verbatim # (lowercase) and deduped. @@ -961,7 +961,7 @@ def test_question_phrases_returns_sliding_n_grams_no_stopword_strip(): def test_question_phrases_n_5_yields_five_token_phrases(): """5-grams trade recall for precision; 'oceania always been at war' is a much stronger Orwell signal than 'always been at war' alone.""" - from aborist.qa.query import _question_phrases + from arborist.qa.query import _question_phrases out = _question_phrases("has oceania always been at war with east asia", n=5) assert "oceania always been at war" in out assert "always been at war with" in out @@ -972,7 +972,7 @@ def test_question_phrases_n_5_yields_five_token_phrases(): def test_question_phrases_skips_when_question_shorter_than_n(): """`who is X?` is too short to yield 4-grams. Empty output is the expected behavior (the body BM25 + title routes still cover it).""" - from aborist.qa.query import _question_phrases + from arborist.qa.query import _question_phrases assert _question_phrases("who is X?", n=4) == [] assert _question_phrases("", n=4) == [] @@ -981,7 +981,7 @@ def test_question_phrases_drops_all_short_token_phrases(): """A window of all 1-3 char tokens is boilerplate ('to be or not') — drops to avoid over-matching. The skip rule fires only when ALL tokens in the window are <4 chars.""" - from aborist.qa.query import _question_phrases + from arborist.qa.query import _question_phrases # All ≤3-char tokens — drop. assert _question_phrases("to be or not", n=4) == [] # Mixed: at least one ≥4-char token → keep. @@ -992,7 +992,7 @@ def test_question_phrases_drops_all_short_token_phrases(): def test_question_phrases_lowercases_and_dedupes(): """Output is lowercase, deduped on string equality. Same sequence in different cases collapses to one phrase.""" - from aborist.qa.query import _question_phrases + from arborist.qa.query import _question_phrases out = _question_phrases("Always been at war Always been at war", n=4) # Repeated sequence appears only once in the output. assert out.count("always been at war") == 1 @@ -1000,7 +1000,7 @@ def test_question_phrases_lowercases_and_dedupes(): def test_search_phrases_returns_empty_on_no_phrases(tmp_path): """Defensive: empty phrase list yields no rows, no exceptions.""" - from aborist.qa.query import _search_phrases + from arborist.qa.query import _search_phrases main_db = tmp_path / "corpus.db" conn = connect(main_db) try: @@ -1014,7 +1014,7 @@ def test_search_phrases_returns_empty_on_no_phrases(tmp_path): def test_search_phrases_skips_phrases_with_double_quotes(tmp_path): """Adversarial input safety: phrases containing `"` would break the FTS5 quoted-phrase syntax. The function silently drops them.""" - from aborist.qa.query import _search_phrases + from arborist.qa.query import _search_phrases main_db = tmp_path / "corpus.db" conn = connect(main_db) try: @@ -1075,7 +1075,7 @@ def test_phrase_route_skipped_when_question_shorter_than_min_n(): tokens to trigger the n=5/n=6 phrase route. Short conventional queries route through body-BM25 + title-LIKE only — phrase routing is structurally biased toward longer allusion-shape questions.""" - from aborist.qa.query import _question_phrases + from arborist.qa.query import _question_phrases # 4 tokens after extraction → empty 5-gram and 6-gram outputs. assert _question_phrases("oceania east asia geography", n=5) == [] assert _question_phrases("oceania east asia geography", n=6) == [] @@ -1136,7 +1136,7 @@ def test_filter_keeps_phrase_match_root_with_no_title_overlap(): """Direct unit test for accept-path 4: a hit whose title shares zero content tokens with the question, but whose document_root is in `phrase_match_roots`, must pass the filter.""" - from aborist.qa.query import _Hit, _filter_by_title_relevance + from arborist.qa.query import _Hit, _filter_by_title_relevance hits = [ # Title shares NO content tokens with the question. Without # accept-path 4 (phrase_match_roots), it would be dropped. @@ -1211,7 +1211,7 @@ def test_unit_search_titles_handles_long_question_without_crash(tmp_path): OR-chain + ORDER BY LENGTH(title) — handles arbitrary token counts. Cap at MAX_TITLE_LIKE_TOKENS bounds the OR-chain growth defensively.""" - from aborist.qa.query import _search_titles + from arborist.qa.query import _search_titles main_db, _qa_db = _build_titled_corpus(tmp_path) conn = connect(main_db) @@ -1239,7 +1239,7 @@ def test_unit_search_titles_handles_long_question_without_crash(tmp_path): def test_unit_search_titles_handles_zero_tokens(): """Defensive: zero tokens → empty list, no SQL executed.""" - from aborist.qa.query import _search_titles + from arborist.qa.query import _search_titles rows = _search_titles(None, [], 32) assert rows == [] @@ -1251,7 +1251,7 @@ def test_unit_search_titles_caps_or_chain_at_max_tokens(tmp_path): expression-tree growth. Beyond the cap, additional tokens are dropped at the SQL layer; the post-filter in the caller sees them all and does the actual filtering.""" - from aborist.qa.query import _search_titles + from arborist.qa.query import _search_titles main_db, _qa_db = _build_titled_corpus(tmp_path) conn = connect(main_db) @@ -1334,7 +1334,7 @@ def test_functional_long_question_returns_sources(tmp_path): def test_unit_hyphen_fold_variants_emits_joined_form(): """`bi-polar` → {"bipolar"}; multiple hyphen runs emit one joined variant each; non-hyphenated input emits nothing.""" - from aborist.qa.query import _hyphen_fold_variants + from arborist.qa.query import _hyphen_fold_variants assert _hyphen_fold_variants("bi-polar is rare?") == {"bipolar"} assert _hyphen_fold_variants("high-school co-op") == { @@ -1349,7 +1349,7 @@ def test_unit_hyphen_fold_variants_emits_joined_form(): def test_unit_title_query_tokens_includes_hyphen_fold_additively(): """Hyphen-fold is additive — `bi-polar is rare?` produces both the split forms (bi, polar) AND the joined form (bipolar).""" - from aborist.qa.query import _title_query_tokens + from arborist.qa.query import _title_query_tokens toks = _title_query_tokens("bi-polar is rare?") assert "bi" in toks @@ -1363,7 +1363,7 @@ def test_unit_title_query_tokens_includes_hyphen_fold_additively(): def test_unit_title_query_tokens_no_hyphen_unchanged(): """Non-hyphen input behaves exactly as before — pin that the fold doesn't add spurious tokens for plain queries.""" - from aborist.qa.query import _title_query_tokens + from arborist.qa.query import _title_query_tokens assert _title_query_tokens("anarchism political philosophy") == { "anarchism", diff --git a/tests/test_reclassify.py b/tests/test_reclassify.py index 0ae0327..9b484e6 100644 --- a/tests/test_reclassify.py +++ b/tests/test_reclassify.py @@ -1,4 +1,4 @@ -"""`aborist reclassify` — re-runs the verifier against live providence records. +"""`arborist reclassify` — re-runs the verifier against live providence records. Covers: - Stale UNGROUNDED records become STRICT/HYBRID/etc when the new verifier @@ -16,13 +16,13 @@ import argparse import json from typing import Iterator -from aborist.cli import _cmd_reclassify -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.qa import query -from aborist.qa.client import StubClient -from aborist.source import Source -from aborist.store import connect +from arborist.cli import _cmd_reclassify +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.qa import query +from arborist.qa.client import StubClient +from arborist.source import Source +from arborist.store import connect class _FakeSource(Source): diff --git a/tests/test_repair.py b/tests/test_repair.py index 9612a39..445c9ea 100644 --- a/tests/test_repair.py +++ b/tests/test_repair.py @@ -19,13 +19,13 @@ from __future__ import annotations from typing import Iterator -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.qa import query -from aborist.qa.client import StubClient -from aborist.qa.repair import mechanical_repair -from aborist.source import Source -from aborist.store import connect +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.qa import query +from arborist.qa.client import StubClient +from arborist.qa.repair import mechanical_repair +from arborist.source import Source +from arborist.store import connect # ---------------------------------------------------------------- mechanical_repair @@ -200,7 +200,7 @@ def test_query_reprompt_rewrites_on_paraphrase_failure(tmp_path): client = _SeqClient() - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY policy = dict(DEFAULT_QUERY_POLICY) policy["repair_enabled"] = True policy["repair_max_reprompts"] = 1 @@ -245,7 +245,7 @@ def test_query_repair_enabled_promotes_synthetic_elision_to_strict(tmp_path): ) # Build a policy variant with repair_enabled=True. - from aborist.qa.query import DEFAULT_QUERY_POLICY + from arborist.qa.query import DEFAULT_QUERY_POLICY policy = dict(DEFAULT_QUERY_POLICY) policy["repair_enabled"] = True diff --git a/tests/test_resume.py b/tests/test_resume.py index c785a62..a30fa4d 100644 --- a/tests/test_resume.py +++ b/tests/test_resume.py @@ -4,10 +4,10 @@ from __future__ import annotations from typing import Iterator -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.source import Source -from aborist.store import connect, get_meta, set_meta +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.source import Source +from arborist.store import connect, get_meta, set_meta class IndexedSource(Source): @@ -54,7 +54,7 @@ def test_resume_skips_processed(tmp_path): conn = connect(db) try: # Pre-seed a high-water as if a prior run got through doc 4. - with __import__("aborist").store.transaction(conn): + with __import__("arborist").store.transaction(conn): set_meta(conn, "source_high_water:indexed_test", "4") src = IndexedSource(n_docs=10) result = ingest_source(conn, src, batch_size=5, resume=True) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index bb742fa..adb65f6 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -10,9 +10,9 @@ import time import pytest -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.snapshot import ( +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.snapshot import ( EMPTY_SNAPSHOT_ROOT, compute_snapshot_root, create_snapshot, @@ -20,8 +20,8 @@ from aborist.snapshot import ( list_snapshots, verify_snapshot, ) -from aborist.source import Source -from aborist.store import connect +from arborist.source import Source +from arborist.store import connect class _FakeSource(Source): diff --git a/tests/test_soft_preflight.py b/tests/test_soft_preflight.py index 95a5138..96d4231 100644 --- a/tests/test_soft_preflight.py +++ b/tests/test_soft_preflight.py @@ -15,7 +15,7 @@ from __future__ import annotations import pytest -from aborist.qa.soft_preflight import ( +from arborist.qa.soft_preflight import ( SOFT_PREFLIGHT_VERSION, SoftPreflightHint, _normalize_label, diff --git a/tests/test_tfidf.py b/tests/test_tfidf.py index fdefbf9..73a89d8 100644 --- a/tests/test_tfidf.py +++ b/tests/test_tfidf.py @@ -5,13 +5,13 @@ from __future__ import annotations import json from typing import Iterator -from aborist.distill import TfidfKeywordDistiller -from aborist.distill.runner import distill_existing -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.merkle import proof_from_dict, verify_proof -from aborist.source import Source -from aborist.store import connect +from arborist.distill import TfidfKeywordDistiller +from arborist.distill.runner import distill_existing +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.merkle import proof_from_dict, verify_proof +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): diff --git a/tests/test_vcs_source.py b/tests/test_vcs_source.py index 466a08f..7bc919e 100644 --- a/tests/test_vcs_source.py +++ b/tests/test_vcs_source.py @@ -14,9 +14,9 @@ from pathlib import Path import pytest -from aborist.ingest import ingest_source -from aborist.sources.vcs import GitRepoSource, MercurialRepoSource -from aborist.store import connect +from arborist.ingest import ingest_source +from arborist.sources.vcs import GitRepoSource, MercurialRepoSource +from arborist.store import connect # --------------------------------------------------------------------------- @@ -120,7 +120,7 @@ def test_git_source_yields_text_files_and_skips_binary(tmp_path): def test_git_source_round_trip_and_supersedes_chain(tmp_path): repo = tmp_path / "demo-repo" _mk_git_repo(repo) - db = tmp_path / "aborist.db" + db = tmp_path / "arborist.db" # First ingest: 2 docs (README.md, src.py). conn = connect(db) diff --git a/tests/test_verify.py b/tests/test_verify.py index 1253060..fcff443 100644 --- a/tests/test_verify.py +++ b/tests/test_verify.py @@ -12,7 +12,7 @@ Tests cover the full trichotomy under each path plus regressions: from __future__ import annotations -from aborist.qa.verify import ( +from arborist.qa.verify import ( extract_claim_spans, extract_proper_nouns, extract_quotes, @@ -360,7 +360,7 @@ def test_entity_path_hybrid_when_some_proper_nouns_unverified(): def test_wikitext_context_strips_markup_before_substring_test(): """Without the strip the verifier sees [[Cloud Strife]] in source and a bare 'Cloud Strife' in the answer's quoted span; substring fails. With - aborist.wikitext.to_base wired into verify_quotes, the context becomes + arborist.wikitext.to_base wired into verify_quotes, the context becomes plain prose and the model's quote verifies. This is the case from the real FF7 query that prompted wiring wikitext-base-v1 into the verifier.""" raw_wikitext_context = ( diff --git a/tests/test_verify_json.py b/tests/test_verify_json.py index 7226748..eb9ce55 100644 --- a/tests/test_verify_json.py +++ b/tests/test_verify_json.py @@ -12,8 +12,8 @@ import json import pytest -from aborist.qa.evidence import EvidenceObject -from aborist.qa.verify import _lenient_json_parse, verify_claim_lattice_json +from arborist.qa.evidence import EvidenceObject +from arborist.qa.verify import _lenient_json_parse, verify_claim_lattice_json # ---------------------------------------------------------------- lenient parser @@ -207,7 +207,7 @@ def test_verify_json_blocks_disallowed_source_role(): def test_claim_title_overlap_passes_when_title_shares_token(): """Rule 8 helper: title shares ≥1 stemmed content token with claim.""" - from aborist.qa.verify import _claim_title_overlap + from arborist.qa.verify import _claim_title_overlap assert _claim_title_overlap( "Homer Simpson's boss is Mr. Burns.", @@ -227,7 +227,7 @@ def test_claim_title_overlap_passes_when_title_shares_token(): def test_claim_title_overlap_fails_on_qcd_for_spin_glass(): """Rule 8 helper: spin-glass case from 2026-05-02. Claim about spin glass cited to Quantum chromodynamics → no token overlap.""" - from aborist.qa.verify import _claim_title_overlap + from arborist.qa.verify import _claim_title_overlap assert not _claim_title_overlap( "Spin glass modeling involves the use of mathematical tensors.", @@ -237,7 +237,7 @@ def test_claim_title_overlap_fails_on_qcd_for_spin_glass(): def test_claim_title_overlap_vacuous_pass_on_empty(): """Defensive: empty title or empty claim → vacuous pass.""" - from aborist.qa.verify import _claim_title_overlap + from arborist.qa.verify import _claim_title_overlap assert _claim_title_overlap("Some claim here.", None) assert _claim_title_overlap("Some claim here.", "") @@ -347,13 +347,13 @@ def test_runner_ask_json_mode_passes_guided_json_extra_body(tmp_path): through the chat client. Stub captures the kwargs so we can assert.""" from typing import Iterator - from aborist.document import Document - from aborist.ingest import ingest_source - from aborist.qa import ask - from aborist.qa.client import StubClient - from aborist.qa.runner import DEFAULT_POLICY - from aborist.source import Source - from aborist.store import connect + from arborist.document import Document + from arborist.ingest import ingest_source + from arborist.qa import ask + from arborist.qa.client import StubClient + from arborist.qa.runner import DEFAULT_POLICY + from arborist.source import Source + from arborist.store import connect class _FakeSource(Source): source_type = "test" diff --git a/tests/test_versioned_ingest.py b/tests/test_versioned_ingest.py index 438b888..ec15108 100644 --- a/tests/test_versioned_ingest.py +++ b/tests/test_versioned_ingest.py @@ -5,10 +5,10 @@ from __future__ import annotations import time from typing import Iterator -from aborist.document import Document -from aborist.ingest import ingest_source -from aborist.source import Source -from aborist.store import connect +from arborist.document import Document +from arborist.ingest import ingest_source +from arborist.source import Source +from arborist.store import connect class FakeSource(Source): diff --git a/tests/test_warrant.py b/tests/test_warrant.py index 8a48bbc..1dc1db8 100644 --- a/tests/test_warrant.py +++ b/tests/test_warrant.py @@ -16,7 +16,7 @@ Covers: from __future__ import annotations -from aborist.qa.warrant import ( +from arborist.qa.warrant import ( _question_is_count_shape, _question_is_entity_list_shape, _question_is_why_shape, diff --git a/tests/test_wikipedia_old.py b/tests/test_wikipedia_old.py index 5ca39aa..3d9f04a 100644 --- a/tests/test_wikipedia_old.py +++ b/tests/test_wikipedia_old.py @@ -7,7 +7,7 @@ from pathlib import Path import pytest -from aborist.sources import WikipediaOldDump, WikipediaSqlDump +from arborist.sources import WikipediaOldDump, WikipediaSqlDump # Minimal fabricated 'old' table dump matching the 2003-05-16 schema. diff --git a/tests/test_wikipedia_xml.py b/tests/test_wikipedia_xml.py index 346afc4..a7eb8cc 100644 --- a/tests/test_wikipedia_xml.py +++ b/tests/test_wikipedia_xml.py @@ -11,13 +11,13 @@ from __future__ import annotations import bz2 from pathlib import Path -from aborist.ingest import ingest_source -from aborist.sources.wikipedia_xml import ( +from arborist.ingest import ingest_source +from arborist.sources.wikipedia_xml import ( WikipediaAbstractDump, WikipediaXmlDump, _strip_ns, ) -from aborist.store import connect +from arborist.store import connect PAGES_ARTICLES_XML = """ @@ -212,7 +212,7 @@ def test_xml_dump_resume_skips_pages_at_or_below_start_id(tmp_path): def test_xml_dump_round_trip_into_sqlite(tmp_path): src = WikipediaXmlDump(_write(tmp_path / "wp.xml", PAGES_ARTICLES_XML)) - db_path = tmp_path / "aborist.db" + db_path = tmp_path / "arborist.db" conn = connect(db_path) try: stats = ingest_source(conn, src) @@ -253,7 +253,7 @@ def test_abstract_dump_extracts_sublinks_as_edges(tmp_path): def test_abstract_dump_round_trip_into_sqlite(tmp_path): src = WikipediaAbstractDump(_write(tmp_path / "abstract.xml", ABSTRACT_XML)) - db_path = tmp_path / "aborist.db" + db_path = tmp_path / "arborist.db" conn = connect(db_path) try: stats = ingest_source(conn, src) diff --git a/tests/test_wikitext.py b/tests/test_wikitext.py index a70f7e9..7651462 100644 --- a/tests/test_wikitext.py +++ b/tests/test_wikitext.py @@ -1,4 +1,4 @@ -"""Unit tests for aborist.wikitext — wikitext-base-v1. +"""Unit tests for arborist.wikitext — wikitext-base-v1. Most tests are tiny synthetic inputs that pin one rule each. The ``test_real_chunk_*`` tests use a real Wikipedia chunk (FF7 characters) @@ -17,7 +17,7 @@ from pathlib import Path import pytest -from aborist.wikitext import BASE_VERSION, extract_wikilinks, to_base +from arborist.wikitext import BASE_VERSION, extract_wikilinks, to_base FIXTURE = Path(__file__).parent / "fixtures" / "ff7_characters_chunk0.wikitext"